# Gariban Designer — Layout Behavior Guide

`pdf-positioner.schema.json` と合わせて読むことで、どんなレイアウトが可能かを把握できます。

---

## 座標系

- 全座標は **mm** 単位で指定（内部で pt に変換: `pt = mm * 72 / 25.4`）
- **原点は左上**、Y 軸は下方向に増加（PDF 内部座標は左下原点だが自動変換される）
- ページ番号は 0-indexed

## 出力モード

### detail — 単一レコード PDF

詳細画面から1レコード分の PDF を出力する。

- `mappings` でレコードの各フィールドを配置
- `subtables` でサブテーブル行を配置
- `relatedRecords` で関連レコードを配置
- `staticTexts` で固定テキストを配置
- `imageMappings` で添付画像を配置
- 1レコード → 1 PDF

### index — 一覧 PDF

一覧画面の全レコードを1つの PDF にまとめる。

- `mappings` の各フィールドが `rowPitch`（mm）間隔で縦に繰り返される
- `rowCount` でページあたりの最大行数を制限（0 = 無制限）
- レコードはページをまたいで連続する（`recordOffset` が累積）
- `pageRowSettings` でページ単位の `rowPitch` / `rowCount` をオーバーライド可能
- `staticTexts` で固定テキスト（ヘッダーラベル等）を配置可能

### combined — 複合 PDF

ヘッダー + 一覧行を1つの PDF に複合出力する。

- `mappings` — **先頭レコードのみ** から描画（ヘッダー部分）
- `subtables` / `relatedRecords` / `imageMappings` — 先頭レコードのみ
- `indexMappings` — **全レコード** を `rowPitch` 間隔で繰り返し描画（一覧部分）
- `staticTexts` — 固定テキスト

---

## テキスト描画パイプライン

フィールド値は以下の順序で加工されてから描画される:

1. **値の取得** — `record[fieldCode].value` を取得
2. **値の正規化** — 配列は `', '` 結合、オブジェクトは `.name` 抽出
3. **フォーマット適用** — `format` 指定がある場合（後述）
4. **prefix / postfix 付加** — `prefix + text + postfix`
5. **制御文字除去** — タブ→スペース変換、その他制御文字除去
6. **overflow 処理** — テキストが枠に収まらない場合の処理
7. **描画** — デュアルフォント（ASCII: Helvetica / CJK: 指定フォント）

### overflow モード

`maxWidth` や `maxHeight` と組み合わせて使用する。

| モード | 動作 |
|--------|------|
| `hidden` | `maxWidth` を超える部分を `...` で切り詰め。改行は `maxHeight` 内に制限 |
| `wrap` | `maxWidth` で自動折り返し。`maxHeight` で行数制限 |
| `shrink` | フォントサイズを 0.5pt ずつ縮小して枠内に収める（最小 4pt） |

**行間**: `fontSize * 1.4`（pt 単位）。フォントサイズの 1.4 倍が行高さ。

### shrink の詳細

1. `maxWidth` がある場合はテキストを折り返し
2. `maxHeight` がある場合は行数が収まるかチェック
3. 収まらなければ fontSize を 0.5pt 減らして再計算
4. 最小 4pt まで縮小

### デュアルフォント

ASCII 文字（`charCode < 128`）は Helvetica、それ以外は指定フォント（NotoSansJP 等）で描画。文字単位でセグメント分割し、各セグメントを適切なフォントで順に描画する。

---

## format — テキストフォーマット

| format 名 | 入力例 | 出力例 | 説明 |
|-----------|--------|--------|------|
| `currency` | `12345` | `¥12,345` | 通貨形式（¥ + カンマ区切り） |
| `number` | `12345` | `12,345` | カンマ区切り数値 |
| `dateSlash` | `2024-03-15` | `2024/03/15` | スラッシュ区切り日付 |
| `dateJP` | `2024-03-15` | `令和6年3月15日` | 和暦日付（元年表記対応） |
| `postcode` | `1234567` | `〒123-4567` | 郵便番号（7桁の数字のみ変換） |

---

## サブテーブル / 関連レコード

テーブル形式でデータを配置する。

- `startY` — テーブル開始 Y 位置（mm）
- `rowHeight` — 行間隔（mm）
- `maxRows` — 最大表示行数（サブテーブル: デフォルト 15、関連レコード: デフォルト 10）
- `columns` — 各カラムの定義

**カラムの継承**: `columns` 内の `startY` / `rowHeight` / `maxRows` を省略すると親テーブルの値を継承する。カラム単位で個別オーバーライドも可能。

各セルは独立したテキスト描画として処理される（罫線は描画されない）。

---

## 画像マッピング

添付ファイルフィールドの最初の画像を PDF に配置する。

- 対応形式: PNG, JPEG
- `x`, `y` は画像の **左上** 位置

| objectFit | 動作 |
|-----------|------|
| `contain` | アスペクト比を維持して枠内に収める（デフォルト） |
| `fill` | 枠のサイズに引き伸ばす（アスペクト比無視） |
| `cover` | `contain` と同じ動作（pdf-lib にクリッピングがないため） |

---

## ページ別行設定（pageRowSettings）

index / combined モードで、ページごとに行レイアウトを変更できる。

```json
{
  "pageRowSettings": {
    "0": { "rowPitch": 15, "rowCount": 5 },
    "1": { "rowPitch": 10, "rowCount": 10, "restart": true }
  }
}
```

- キーはページ番号の文字列（`"0"`, `"1"`, ...）
- 指定のないページはグローバルの `rowPitch` / `rowCount` を使用
- **レコード連続**: デフォルトでは前ページの続きからレコードを描画
- **restart**: `true` にするとそのページでレコードオフセットを 0 にリセット

---

## 出力ファイル名トークン

| トークン | 使用可能モード | 説明 |
|---------|--------------|------|
| `{TODAY}` | 全モード | 当日日付 `YYYYMMDD` |
| `{レコード番号}` | detail, combined | レコード番号 |
| `{$id}` | detail, combined | レコード ID |
| `{任意のフィールドコード}` | detail, combined | 対象レコードのフィールド値 |

- combined では先頭レコードの値を使用
- index では `{TODAY}` のみ使用可能
- ファイル名の禁則文字（`\ / : * ? " < > |`）は `_` に置換

---

## テンプレート URL

| 値 | 動作 |
|----|------|
| 通常の URL | PDF ファイルをテンプレートとして読み込み |
| `__blank__` | A4 サイズ（210 x 297 mm）の空白 PDF を生成 |

---

## 値の正規化

kintone フィールドの型に応じて自動変換される:

| フィールド型 | 変換 |
|------------|------|
| テキスト / 数値 | そのまま文字列化 |
| ユーザー選択 | `.name` プロパティ（表示名） |
| 複数選択 / チェックボックス | カンマ区切り結合（`A, B, C`） |
| オブジェクト | `.name` があれば名前、なければ JSON 文字列 |
| null / undefined | 空文字列 |

---

## PDF テンプレート・フォントのホスティング

pdf-positioner が使用する PDF テンプレートとフォントは Cloudflare Worker がホスティングする。リソースの種類によって CORS ポリシーが異なる。

### CORS ポリシー

| リソース | Access-Control-Allow-Origin | 備考 |
|----------|---------------------------|------|
| JS / フォント | `*`（制限なし） | 誰でも取得可能 |
| PDF テンプレート | 具体的 origin のみ | Worker の KV に登録された origin だけ許可 |

PDF テンプレートは機密性が高いため、許可する origin を明示的に登録する必要がある。

### kintone ドメインの登録

kintone から PDF テンプレートを取得するには、kintone のドメイン（例: `xxxxx.cybozu.com`）を Worker 管理画面で許可 origin として登録する。登録がないと CORS エラーでテンプレート取得に失敗する。

### 取得の仕組み

- pdf-positioner.js は単純な `fetch(url)` でテンプレートやフォントを取得する
- CORS の処理はブラウザと Worker が行うため、設定 JSON 側で特別な対応は不要
- `templateUrl` / `fontUrl` は **HTTPS 必須**（localhost 開発時は自動で Worker ドメインに変換される）
- `__blank__` テンプレート使用時は PDF テンプレートの取得をスキップし、フォントのみ取得する

### Cloudflare Worker を使わない場合

任意の Web サーバー（nginx, Apache, S3 + CloudFront 等）で PDF テンプレートやフォントをホストできる。以下の要件を満たすこと。

#### 必須要件

1. **HTTPS で配信する** — ブラウザの Mixed Content 制約により HTTP では取得に失敗する
2. **CORS レスポンスヘッダーを設定する** — kintone はクロスオリジンでリソースを取得するため、以下のヘッダーが必要:
   - `Access-Control-Allow-Origin` — kintone ドメイン（例: `https://xxxxx.cybozu.com`）を指定
   - `Access-Control-Allow-Methods: GET, OPTIONS`
   - `Access-Control-Allow-Headers: Content-Type`
3. **正しい `Content-Type` を返す** — MIME タイプが正しくないとブラウザが処理に失敗する場合がある:
   - PDF テンプレート: `application/pdf`
   - OTF フォント: `font/otf`
   - TTF フォント: `font/ttf`

#### nginx 設定例（最小限）

```nginx
server {
    listen 443 ssl;
    server_name assets.example.com;

    location /pdf-assets/ {
        # CORS
        add_header Access-Control-Allow-Origin "https://xxxxx.cybozu.com" always;
        add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type" always;

        # preflight（if ブロック内では add_header を再指定する必要がある）
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin "https://xxxxx.cybozu.com" always;
            add_header Access-Control-Allow-Methods "GET, OPTIONS" always;
            add_header Access-Control-Allow-Headers "Content-Type" always;
            return 204;
        }

        # MIME types
        types {
            application/pdf  pdf;
            font/otf         otf;
            font/ttf         ttf;
        }

        root /var/www;
    }
}
```
