Files
docview/docs/design/05-data-contracts.md
T
2026-09-21 13:41:40 +09:00

10 KiB

05 データ・インターフェース設計

設計原則

文書の位置は形式ごとの意味を保持した直和型で表現する。内部のページ番号は0始まり、画面の物理ページ番号は1始まりとする。ページラベルは文字列であり数値として計算しない。ドメインモデルはQtやPDFiumのオブジェクトを直接含めず、非同期操作の全応答に文書の世代を付ける。

主要モデル

モデル 主なフィールド 制約・意味
DocumentIdentity documentId、canonicalPath、fileIdentity、size、mtime、contentHash? documentIdは端末内UUID。物理ファイルIDは利用可能時のみ。ハッシュ未計算を許容
DocumentSession sessionId、generation、format、state、capabilities sessionIdは起動ごとの予測困難な識別子。保存IDとは別
DocumentMetadata title、author?、language?、pageCount?、spineCount? 未判明と0を区別。文字列長を制限しUIへプレーンテキスト表示
PageInfo pageIndex、label?、widthPt、heightPt、cropBox、rotation PDFの元座標と表示変換を保持
TocNode id、parentId?、title、target?、source、children sourceはpdf-outline / epub-nav / epub-ncx / html-toc / html-heading / spine
Heading id、level?、title、target、source、precision level不明可。precisionはexact / page / approximate
Link region?、target、label?、kind kindはinternal / external / blocked。文字列をコマンドとして実行しない
ViewState location、zoomMode、zoomValue、rotation、panels、focus ズームはpercent / fit-width / fit-page。本文フォントサイズとは別
ReaderState documentId、savedLocation、updatedAt、formatVersion 文書が変わった場合は位置の再解決が必要

配列は必要に応じページングして応答する。巨大な目次や数万項目を一度にUIへ投入しない。titleの空白・制御文字を整理する際も原本は変更しない。

位置の表現

型 保存する値 再解決
PdfLocation pageIndex、pageLabel?、xPt、yPt、viewRotation、fitMode PDFユーザー空間の位置をCropBox・回転・DPRで画面へ変換。変更文書ではページ範囲を検証
HtmlLocation resourcePath、fragment?、domPath?、textQuote?、progression fragment→DOMと文字列照合→資源内進捗の順。progressionは0〜1
EpubLocation packagePath、spineIdref、href、cfi?、fragment?、textQuote?、progression パッケージ内のspine→CFI→fragment→文字列→進捗の順で解決

PDFのxPt/yPtは元PDF座標系を明示して保存し、画面左上基準のCSS座標とは混ぜない。常に変換行列と逆行列を使う。CropBox外の指定は最寄りの可視領域へ調整し、精度をpageまたはapproximateとして通知する。

HTML/EPUBのスクロール軸はwriting-modeとdirectionから決める。progressionは読み進めた割合であり、scrollTop/scrollHeightだけでは定義しない。書体や幅が変わっても文字列アンカーを先に試す。レイアウト完了前の仮サイズから復元位置を確定しない。

textQuoteは短い前後文脈を持つが保存を無効化できる。ローカル状態には文書パスと短い本文断片が含まれ得るため、06の履歴消去対象に含める。スクリプト、DOMオブジェクト、任意XPath評価式は保存しない。

能力情報

能力はsupported / unavailable / loading / unsupportedの4状態を基本とし、単純な真偽値で「未取得」と「存在しない」を同一視しない。

能力 意味
pageNavigation 安定した物理ページに移動できる
chapterNavigation spineや複数HTML資源に沿った移動がある
outline 文書に目次として使える構造がある
headings 本文または構造情報から節の起点を得られる
textSearch 既存文字情報を検索できる。画像PDFではunavailableになる場合がある
textSelection 内容が文字選択を許容し、テキストを取得できる
reflow 本文の文字サイズ・行幅変更を反映できる
password 現在パスワードを要求している

unsupportedには理由コードを付ける。UIは理由をヘルプ/一時通知に出し、無効ボタンを常時並べない。

FormatAdapterの論理契約

以下は実装言語に依存しないインターフェース定義であり、アプリケーションコードではない。操作は非同期、sessionIdと取消しトークンを受け取る。

操作 入力 結果 特記事項
probe 制限付きファイル情報・先頭バイト format、confidence、理由 ZIP内部の詳しい判定は隔離側
open DocumentSource、policy、password? session、metadata、capabilities パスワード再入力と破損を区別
getOutline cursor、limit TocNodeの部分集合、nextCursor 同一文書で安定したID
getHeadings resource/page範囲、cursor Headingの部分集合、完了状態 抽出途中のジャンプ要求は待機/取消し可能
resolveLocation 保存位置、復元方針 解決済み位置、precision 失敗時は先頭へ黙って移さず通知
navigate target、alignment 到達位置、renderRevision jump履歴への記録はControllerが担当
renderPdfTiles page、matrix、clip、revision TileBatch PDF専用描画ポート。WebEngineは可視域通知ポートを使う
setViewport size、DPR、readingStyle layoutRevision reflow後に位置再解決
findText query、direction、cursor、limit match位置、nextCursor 提案機能。平文検索が既定
close sessionId released 繰返し呼出しは無害

共通契約は操作の意図をそろえるためのもので、PDFの画像タイルとHTMLのDOMを同一の描画データへ変換しない。

コマンドと入力の境界

Command RegistryはcommandId、引数スキーマ、実行可能モード、必要な能力、取消し可否を持つ。Input Routerからはキー名そのものではなく検証済みCommandを受け取る。マウス操作も同じCommandを経由する。

コマンド入力欄は定義済み文法だけを解釈する。シェル、外部コマンド、JavaScriptの実行機能は持たない。パスの引用符は構文として扱うが、変数展開・コマンド置換・グロブ実行はしない。:openのパスは明示操作による新しい許可対象としてBrokerで検証する。

プロセス間メッセージ

IPCは長さ付きCBORフレームをローカル専用チャネルで送る。外部待受ポートを開かない。子プロセスの標準出力を信頼済みのコマンドストリームとして解釈しない。実装時にパイプ/ローカルソケットを選び、OSごとのアクセス制御を適用する。

フィールド 内容
protocolVersion 整数。未知版は接続を拒否し依存不整合を報告
requestId セッション内一意、応答・取消しの対応づけ
sessionId / generation 文書と世代。旧文書の応答を破棄
operation ホワイトリスト列挙。任意メソッド名を実行しない
payload 操作別の有限スキーマ。整数範囲・文字列長・配列数を検証
result / error どちらか一方。errorはコード、再試行可否、利用者向け説明キー

制御フレームの上限は1 MiB、PDFタイル/資源データは別の分割チャネルで1片8 MiB以下を提案する。1タイルの最大寸法・stride・ピクセル形式・バッファ長を受信側で再検証し、乗算の桁あふれを拒否する。数値は08で測定し、07の入力制限と合わせて変更する。

共有メモリを使う場合はBrokerが割り当てた短命のバッファIDだけをワーカーへ渡し、サイズ上限・所有セッション・読書き方向を管理する。ワーカーが示す任意パスやハンドル値を主プロセスの権限で開かない。close/取消し/クラッシュ時に参照数を解放し、解放済み応答を表示しない。

状態と遷移

stateDiagram-v2
  [*] --> Empty
  Empty --> Opening: open
  Ready --> Opening: open another
  Opening --> PasswordRequired: encrypted
  PasswordRequired --> Opening: submit
  Opening --> Ready: initial content
  Opening --> Failed: invalid or unsupported
  Ready --> Recovering: worker crash
  Recovering --> Ready: explicit retry
  Recovering --> Failed: retry fails
  Ready --> Closing: close
  Closing --> Empty: resources released

OpeningとPasswordRequiredの取消しは、旧文書があればReadyへ戻し、なければEmptyへ戻す。図のFailedはエラー状態を表し、元文書を保持している場合はエラー表示後に旧Readyへ復帰できる。終了要求はどの状態からでも受け付ける。

ReadyにはnavigationIndex=loadingという独立した副状態を持てる。スクロール・ズームのたびにOpeningへ戻さない。パネル表示は文書状態と独立し、読み込み途中に目次を開いても競合しない。

整合性と取消し

  • 文書切替でgeneration、倍率や向きの変更でrenderRevision、HTML/EPUBの再配置でlayoutRevisionを進める。
  • PDFium処理は直列キューとし、取消し要求は予約済み処理を除去する。実行中の処理は安全な区切りで止める。
  • closeは読取チャネルの失効→新規要求停止→実行処理取消し→ワーカー終了→一時資源回収の順。停止不能時は期限付きで強制終了する。
  • 読書位置の保存は最後に画面へ反映された位置だけを対象とする。未到達のジャンプ要求で保存位置を更新しない。
  • 戻る/進む履歴は明示ジャンプとリンク移動を単位に記録する。連続スクロールの全フレームは記録しない。

エラーの責務

ワーカーは技術的な原因と位置を返し、Controllerは回復操作へ翻訳する。UIへOSの例外やスタックトレースをそのまま表示しない。利用者が必要とするファイル名・ページ・理由は表示し、ログ側ではパスや本文を原則省く。コード体系と回復策は07を正とする。