# 帳票設計書 2-ANSI制御シーケンス付きテキスト出力

## 概要

本ドキュメントは、Windows Terminal のテキストバッファから選択範囲のテキストをANSIエスケープコード（色・スタイル情報）付きで取得する処理の設計を記述する。

### 本帳票の処理概要

テキストバッファ（TextBuffer）上でユーザーが選択した範囲のテキストデータを、ANSIエスケープシーケンス（SGR制御コード）を含む形式で抽出・出力する。文字の色、太字、斜体、下線、取り消し線などのスタイル情報がエスケープコードとして埋め込まれる。

**業務上の目的・背景**：ターミナル出力の色やスタイル情報を保持したままテキストをコピーするニーズに応える。コマンド出力のシンタックスハイライトやログの色分け情報を、他のANSI対応ターミナルやテキストエディタに貼り付ける際に、表示スタイルを維持できるようにする。

**帳票の利用シーン**：ユーザーがターミナル画面上でテキストを選択し、ANSI制御シーケンス付きでクリップボードにコピーする際に使用される。CopySelectionToClipboard の withControlSequences パラメータが true の場合にこの出力が使用される。

**主要な出力内容**：
1. 選択範囲内の各行のテキスト文字列にANSIエスケープコードを付与したUnicode文字列
2. SGR（Select Graphic Rendition）制御シーケンス（色、太字、斜体、下線スタイル等）
3. 行間の改行コード（\r\n）
4. ダブル幅/ダブル高さの行レンディション情報（ESC #3, #4, #6）
5. ハイパーリンク情報（OSC 8エスケープシーケンス）

**帳票の出力タイミング**：ユーザーがテキスト選択後にANSIシーケンス付きコピー操作を行った際に出力される。またバッファエクスポート（SerializeTo）でも同様の制御シーケンス付き出力が使用される。

**帳票の利用者**：ANSI対応ターミナル間でスタイル付きテキストを共有したい開発者・システム管理者。

## 帳票種別

テキストデータ出力（ANSI制御シーケンス付きクリップボードコピー / ファイルシリアライズ）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | ターミナルペイン | - | テキスト選択 + ANSI付きコピー操作（withControlSequences=true） |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | ANSIエスケープコード付きテキスト（メモリ上 std::wstring） |
| 用紙サイズ | 該当なし |
| 向き | 該当なし |
| ファイル名 | 該当なし（クリップボード経由）/ エクスポート時は .txt |
| 出力方法 | クリップボードへのコピー / ファイル書き込み |
| 文字コード | UTF-16LE（wstring）/ エクスポート時はUTF-16LE（BOM付き） |

### PDF固有設定

該当なし

### Excel固有設定

該当なし

## 帳票レイアウト

### レイアウト概要

ANSI制御シーケンス付きテキスト出力は、各テキスト文字の前にSGR制御コードが挿入された構造を持つ。属性が変化するたびにエスケープシーケンスが挿入される。

```
┌──────────────────────────────────────────────┐
│ [行レンディション]                              │
│ ESC[属性変更m テキスト ESC[属性変更m テキスト...  │
│ \r\n                                          │
│ [行レンディション]                              │
│ ESC[属性変更m テキスト...                       │
│ ESC[0m (リセット)                              │
└──────────────────────────────────────────────┘
```

### ヘッダー部

該当なし

### 明細部

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|------|
| 1 | 行レンディション | ダブル幅・ダブル高さの行制御 | ROW::GetLineRendition() | ESC #3/4/6 | 可変 |
| 2 | SGR属性 | 文字の色・スタイル情報 | TextAttribute | ESC[Nm 形式 | 可変 |
| 3 | 前景色 | 文字の前景色 | TextAttribute::GetForeground() | SGR 38/39 形式 | 可変 |
| 4 | 背景色 | 文字の背景色 | TextAttribute::GetBackground() | SGR 48/49 形式 | 可変 |
| 5 | 下線色 | 下線の色 | TextAttribute::GetUnderlineColor() | SGR 58/59 形式 | 可変 |
| 6 | テキスト | 各属性ランの文字列 | ROW::GetText(x, nextX) | プレーンテキスト | 可変 |
| 7 | 改行コード | 行間区切り | 固定値 | \r\n | 2バイト |

### フッター部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | リセットシーケンス | 最終行の末尾で属性をリセット | 固定値 | ESC[0m |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| 選択開始位置（beg） | テキスト選択の開始座標（x, y） | Yes |
| 選択終了位置（end） | テキスト選択の終了座標（x, y） | Yes |
| withControlSequences | ANSI制御シーケンスを含めるフラグ | Yes（true） |
| ブロック選択モード（blockSelection） | 矩形選択か通常選択か | No（デフォルト: false） |
| 末尾空白トリミング（trimTrailingWhitespace） | 行末の空白文字を除去するか | No（デフォルト: true） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 行番号（y座標） | 昇順 |
| 2 | 属性ラン順序 | 昇順（左から右） |

### 改ページ条件

該当なし

## データベース参照仕様

### 参照テーブル一覧

該当なし（メモリ上の TextBuffer から直接データを取得する）

### テーブル別参照項目詳細

#### TextBuffer（メモリ上バッファ）

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| ROW::GetLineRendition() | 行レンディション | 毎行 | SingleWidth以外の場合のみ出力 |
| ROW::Attributes().slice().runs() | 属性ラン一覧 | startX ~ endX | 各ランの属性値と長さ |
| TextAttribute::GetCharacterAttributes() | SGR属性フラグ | 各ラン | Intense, Italics, Blinking等 |
| TextAttribute::GetForeground() | 前景色 | 各ラン | TextColor型 |
| TextAttribute::GetBackground() | 背景色 | 各ラン | TextColor型 |
| TextAttribute::GetUnderlineColor() | 下線色 | 各ラン | TextColor型 |
| TextAttribute::GetHyperlinkId() | ハイパーリンクID | 各ラン | 0の場合リンクなし |
| ROW::GetText(x, nextX) | テキスト | 各ラン | 属性ランに対応する文字列 |
| ROW::MeasureRight() | 最終文字位置 | 毎行 | 末尾空白トリミング用 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| attrDelta | attr ^ previousAttr | 該当なし | 前の属性との差分をXORで計算 |
| SGR番号 | Mappingテーブル参照 | 該当なし | 属性種別ごとにON/OFFのSGR番号を対応付け |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[コピー操作要求 withControlSequences=true] --> B[CopySelectionToClipboard]
    B --> C[RetrieveSelectedTextFromBuffer]
    C --> D[CopyRequest::FromConfig でリクエスト構築]
    D --> E[TextBuffer::GetWithControlSequences 呼び出し]
    E --> F{beg > end?}
    F -->|Yes| G[空文字列を返却]
    F -->|No| H[行ループ開始]
    H --> I[GetRowByOffset で行取得]
    I --> J[_RowCopyHelper で行範囲計算]
    J --> K[_SerializeRow で行シリアライズ]
    K --> L[属性差分を計算し SGR コード生成]
    L --> M[テキスト文字列を追加]
    M --> N{改行を追加する?}
    N -->|Yes| O[\\r\\n を追加]
    N -->|No| P[次の行へ]
    O --> P
    P --> Q{最終行?}
    Q -->|No| H
    Q -->|Yes| R[selectedText を返却]
    R --> S[WriteToClipboard イベント発火]
    S --> T[終了]
    G --> T
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 選択なし | IsSelectionActive() が false | なし | CopySelectionToClipboard が false を返す |
| 無効な範囲 | beg > end | なし | 空文字列を返却 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 最大9001行 |
| 目標出力時間 | 即座（同期処理）、プレーンテキストより若干のオーバーヘッドあり |
| 同時出力数上限 | 1（ターミナルロック取得による排他制御） |

## セキュリティ考慮事項

- テキストバッファへのアクセスは `_terminal->LockForWriting()` によるロックで排他制御される
- ANSIエスケープシーケンスを含むテキストを他のターミナルに貼り付ける場合、悪意あるエスケープシーケンスによる攻撃リスクが理論上存在する（ただし本処理は出力側のみ）

## 備考

- _SerializeRow は GetWithControlSequences と SerializeTo（ファイルエクスポート）の両方から呼び出される共通処理である
- 属性の差分計算（XOR）により、変化した属性のみSGRコードを出力する効率的な実装となっている
- Intense と Faint の同時解除には特別な処理（SGR 22）が必要であり、専用の分岐が存在する
- ハイパーリンク情報は OSC 8 エスケープシーケンスとして出力される

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

属性情報の管理構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | textBuffer.hpp | `src/buffer/out/textBuffer.hpp` | CopyRequest 構造体（204-271行目）のフィールドを理解する |
| 1-2 | TextAttribute.hpp | `src/buffer/out/TextAttribute.hpp` | TextAttribute クラスのフィールド（CharacterAttributes, Foreground, Background, UnderlineColor, HyperlinkId）を理解する |

**読解のコツ**: CharacterAttributes は各種スタイルフラグ（Intense, Italics, Blinking, CrossedOut, ReverseVideo等）をビットフラグで管理している。XOR演算（^）による差分計算がシリアライズの核心。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | CopySelectionToClipboard（1343-1372行目）の withControlSequences 分岐 |
| 2-2 | TerminalSelection.cpp | `src/cascadia/TerminalCore/TerminalSelection.cpp` | RetrieveSelectedTextFromBuffer（962-1007行目）の980-983行目でwithControlSequences時にGetWithControlSequencesを呼び出す分岐 |

**主要処理フロー**:
1. **980行目**: withControlSequences が true の場合
2. **982行目**: textBuffer.GetWithControlSequences(req) を呼び出し

#### Step 3: GetWithControlSequences を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | textBuffer.cpp | `src/buffer/out/textBuffer.cpp` | GetWithControlSequences メソッド（2172-2198行目）。行ループと _SerializeRow 呼び出し |

**主要処理フロー**:
- **2179-2181行目**: selectedText, previousTextAttr, delayedLineBreak の状態変数を初期化
- **2186行目**: 行ループ（firstRow から lastRow まで）
- **2190行目**: _RowCopyHelper で行範囲を計算
- **2194行目**: _SerializeRow で行をシリアライズ

#### Step 4: _SerializeRow の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | textBuffer.cpp | `src/buffer/out/textBuffer.cpp` | _SerializeRow メソッド（2687行目以降）。行レンディション出力、属性ラン走査、SGRコード生成、テキスト出力の詳細 |

**主要処理フロー**:
- **2689-2698行目**: 行レンディション（DoubleWidth/DoubleHeight）のエスケープシーケンス出力
- **2700-2702行目**: 属性ランの取得（slice + runs）
- **2713行目**: 属性ランのループ
- **2728行目**: 前の属性との差分比較（attr XOR previousAttr）
- **2739-2755行目**: Intense/Faint 同時解除の特別処理（SGR 22）
- **2757-2781行目**: 各種スタイル属性のSGR番号マッピング

### プログラム呼び出し階層図

```
ControlCore::CopySelectionToClipboard (ControlCore.cpp:1343)
    |
    +-- Terminal::RetrieveSelectedTextFromBuffer (TerminalSelection.cpp:962)
    |       |
    |       +-- CopyRequest::FromConfig (textBuffer.hpp:235)
    |       |
    |       +-- TextBuffer::GetWithControlSequences(req) (textBuffer.cpp:2172)
    |               |
    |               +-- TextBuffer::_RowCopyHelper (textBuffer.cpp:2088)
    |               |
    |               +-- TextBuffer::_SerializeRow (textBuffer.cpp:2687)
    |                       |
    |                       +-- ROW::GetLineRendition()
    |                       +-- ROW::Attributes().slice().runs()
    |                       +-- TextAttribute 差分計算 (XOR)
    |                       +-- SGRコード生成 (Mapping テーブル)
    |                       +-- ROW::GetText(x, nextX)
    |
    +-- WriteToClipboard.raise (ControlCore.cpp:1365)
```

### データフロー図

```
[入力]                          [処理]                              [出力]

TextBuffer (メモリ)        ---> GetWithControlSequences()      ---> std::wstring (selectedText)
  |                                |                                  |
  +-- ROW[]                      +-- _RowCopyHelper()               +-- ANSIエスケープコード
  +-- TextAttribute[]            +-- _SerializeRow()                    +-- SGR属性
  +-- 選択範囲(beg,end)          |     +-- 属性差分計算               +-- テキスト
                                 |     +-- SGRコード生成              +-- 改行コード
                                 |     +-- テキスト文字列追加
                                 +-- 改行コード付加
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| textBuffer.hpp | `src/buffer/out/textBuffer.hpp` | ヘッダー | TextBuffer クラス定義、CopyRequest 構造体定義 |
| textBuffer.cpp | `src/buffer/out/textBuffer.cpp` | ソース | GetWithControlSequences、_SerializeRow の実装 |
| TextAttribute.hpp | `src/buffer/out/TextAttribute.hpp` | ヘッダー | TextAttribute クラス定義 |
| ControlCore.cpp | `src/cascadia/TerminalControl/ControlCore.cpp` | ソース | CopySelectionToClipboard のエントリーポイント |
| TerminalSelection.cpp | `src/cascadia/TerminalCore/TerminalSelection.cpp` | ソース | RetrieveSelectedTextFromBuffer の実装 |
| Terminal.hpp | `src/cascadia/TerminalCore/Terminal.hpp` | ヘッダー | Terminal クラス定義 |
| ControlInteractivity.cpp | `src/cascadia/TerminalControl/ControlInteractivity.cpp` | ソース | ユーザー操作からのコピートリガー |
