initial commit
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
# 03 フォーマット・取込み設計
|
||||
|
||||
DocView アプリケーション設計書 | 版 1.0 | 2026-09-19
|
||||
|
||||
対象要件: R01-R04、R06、R07、R09、R11、R14。
|
||||
|
||||
## 1. 対応方針
|
||||
|
||||
PDFを最優先とし、PDFiumを呼ぶ専用PDFWorkerで描画する。HTMLとEPUBの本文はQt WebEngineで描画する。TUI風の配色・枠線・文字中心の操作UIはアプリの領域に適用し、PDFや著者指定の本文レイアウトを変更しない。
|
||||
|
||||
| 形式 | 初期リリースの対応範囲 | 制約の伝え方 |
|
||||
|---|---|---|
|
||||
| PDF | 読取り専用表示、拡大縮小、回転、ページ一覧、outline、リンク | 対応限界・破損を文書情報とエラーで明示 |
|
||||
| 単独HTML | ローカルHTMLと許可された文書フォルダー内のCSS・画像・font・リンク | 外部依存資源とスクリプト依存部分は表示できない旨を通知 |
|
||||
| ZIP化HTML | `index.html` と相対参照資源を内部展開して表示 | 入口不明時は候補選択。危険なアーカイブは取込み中止 |
|
||||
| EPUB 2 / 3 | DRMのない静的本文、NCX/nav、spine、縦書き、RTL、リフロー、固定レイアウト | 音声・動画・media overlays・動的対話は初期版対象外 |
|
||||
|
||||
静的閲覧、DRM非対応、音声・動画非対応は設計上の初期範囲であり、原要件から直接指定された制限ではない。EPUBの全機能適合を宣言するものではない。PDFの編集、注釈追加、フォーム入力、保存による文書変更は行わない。既存の可視要素の表示は、編集機能とは分けて扱う。
|
||||
|
||||
## 2. 共通の取込み手順
|
||||
|
||||
1. MainのFileBrokerが利用者の選んだファイルを読取り専用で開く。拡張子と固定長のシグネチャを候補判定に利用するが、複雑な内容解析は行わない。
|
||||
2. PDFはPDFWorker、ZIP/EPUBはArchiveWorkerへ読取り専用handleを渡す。単独HTMLは許可rootと資源IDを登録する。形式の確定は担当の解析器で行う。
|
||||
3. Workerが返すメタデータは、Mainでも型・長さ・件数・参照IDを検証する。Workerは信頼境界の内側ではない。
|
||||
4. 新しい文書セッションに世代番号と推測困難なtokenを割り当てる。1ウィンドウのアクティブ文書は1つとする。切替候補の入口検証中は旧文書を維持し、新文書のコミット時に旧tokenと旧要求を失効させる。
|
||||
5. 最初の表示位置に必要な情報・資源を優先して読み込む。目次とページ一覧は段階的に更新する。取込み・描画は取消可能とする。
|
||||
6. 正常終了、文書切替、取消、失敗のすべてでhandle、共有画像、仮想URL、展開資源を解放する。画面に残っている旧文書の結果を新文書へ混ぜない。
|
||||
|
||||
位置とナビゲーションの形式非依存モデルは [データ・内部契約設計](05-data-contracts.md)、隔離と入力制限は [セキュリティ・異常系設計](07-security-errors.md) に定義する。
|
||||
|
||||
## 3. PDFの表示契約
|
||||
|
||||
### 3.1 正確性
|
||||
|
||||
PDFのページ寸法、CropBox/MediaBox、ページ回転、font、文字配置、画像、透明度、クリッピング、注釈外観を尊重する。座標はPDF文書座標を基準に保持し、device pixel ratio、ズーム、表示回転を一つの変換で適用する。CropBox左下が原点でないページ、文書のRotateと利用者の回転が重なるページも同じ変換行列で扱う。リンク矩形と文字選択の当たり判定はその逆行列を利用する。ページ本文をHTMLへ変換して表示しない。
|
||||
|
||||
画面は必要解像度のページ/タイル画像を使用する。倍率は論理画面上で計算し、DPRを一度だけ掛けて描画バッファ寸法を得る。低解像度の暫定画像を拡大表示している間は置換用の再描画を要求する。文字・図形の欠落、既存注釈の欠落、透明度の破綻は「表示できた」と判定しない。注釈の静的外観とフォームwidget外観は必要な描画経路を分けて確認し、編集を無効にしただけで外観も消える状態を避ける。具体的な評価方法は試験設計とPDF技術判断に従う。
|
||||
|
||||
未埋込みfontにはOS差が残り得る。代替fontの集合と探索順を管理し、両OSの比較対象に含める。任意PDFに対する完全一致を保証せず、検証済みの互換性範囲をリリースごとに記録する。
|
||||
|
||||
### 3.2 注釈・フォーム外観・フォント
|
||||
|
||||
通常のページ描画へFPDF_ANNOTを付けるだけではwidgetとpopupは描画されない。保存済みフォーム外観は、PDFiumのform environmentを用いるFPDF_FFLDraw系の描画経路を別途用意し、ページ本文と同じ回転・縮尺・タイル原点へ合わせて合成する。form environmentの初期化、ページ読込/破棄通知、終了処理はワーカー内で管理する。フォーム入力、action実行、JavaScript、保存APIは公開しない。タイル描画とフォーム描画の座標が一致しない場合は、可視領域単位の合成方式へ修正する。[PDFium描画API](https://pdfium.googlesource.com/pdfium/+/main/public/fpdfview.h)、[フォーム描画API](https://pdfium.googlesource.com/pdfium/+/main/public/fpdf_formfill.h)
|
||||
|
||||
既存外観が欠落したフォームは、暗黙にユーザー入力や文書保存で補修せず、表示できない可能性を通知する。popup内の注釈本文は選択時の読取り専用補助表示として扱い、紙面外に隠れたコメントの存在を見落とさない。静的注釈、widget、popup、appearance欠落をT18とG-RENDERで個別確認する。
|
||||
|
||||
埋込fontを優先し、未埋込みfontは許諾を確認した同梱候補と明示的なOS font索引を用いる。OS font索引はBrokerが管理し、PDFWorkerへは選択済みfontの読取り専用handleまたはバイト列だけを渡す。font探索目的でWorkerの任意パスアクセスを許可しない。共通font集合を固定しても、元fontが欠けた文書の完全一致を保証するものではない。
|
||||
|
||||
### 3.3 ナビゲーション
|
||||
|
||||
- PDF outlineは目次として保持し、明示destinationsとnamed destinationsをページ位置へ解決する。
|
||||
- タグ構造に有効なH/H1-H6があれば見出しジャンプを提供する。矩形まで解決できれば見出し位置、ページだけ解決できる場合はページ先頭へ移動し精度を表示する。見出しからページへの対応も解決不能なら該当項目に理由を付け、全体を「見出しなし」としない。座標が得られずページだけ分かる場合はprecision=pageで移動する。有効な構造見出しがない場合は文書内宛先を持つoutlineを代用し、「目次項目移動」と表示する。目次そのものはoutlineを使用する。文字サイズだけから推測した見出しは初期版で生成しない。
|
||||
- ページ番号は内部の0始まりindexと画面の1始まり表示を分ける。文書のPageLabelsがあれば併記し、`iv` や `1` と物理ページ番号を混同しない。
|
||||
- ページ内リンクは現在の文書へ移動する。外部HTTP(S)リンクは宛先を提示し、利用者の明示操作で既定ブラウザーへ渡す。Launch action、JavaScript action、自動外部起動は実行しない。
|
||||
- パスワードが必要な文書は専用入力を求め、値はメモリーだけに保持する。DRM等の未対応保護や未対応機能は理由を返す。
|
||||
|
||||
## 4. 単独HTMLとZIP化HTML
|
||||
|
||||
### 4.1 入口の選択
|
||||
|
||||
単独HTMLは選んだファイルを入口、その親ディレクトリーを初期rootとする。`../assets` 等でroot外の資源が必要な場合、許可範囲を自動拡大しない。「文書フォルダーを指定」で利用者が共通親を指定した場合だけrootを変更する。ブラウザーには絶対ローカルパスを渡さない。
|
||||
|
||||
ZIPの入口は次の順序で決める。
|
||||
|
||||
| 条件 | 動作 |
|
||||
|---|---|
|
||||
| root直下に完全一致の `index.html` がある | 採用 |
|
||||
| rootにはなく、共通トップディレクトリー直下に `index.html` が一つある | 採用 |
|
||||
| 上記以外でHTML候補がある | 相対パス一覧からキーボードで選択 |
|
||||
| HTML候補がない | `E_HTML_ENTRY_MISSING` |
|
||||
|
||||
`index.htm`、大小文字違いの名称、複数の深いディレクトリー内のindexは候補一覧で扱い、勝手に最初の候補へ決めない。選択結果は文書fingerprintと関連付ける。ZIPでは入口が深い場所にあってもZIP全体をrootとし、正当な相対参照を維持する。
|
||||
|
||||
### 4.2 内部展開
|
||||
|
||||
ArchiveWorkerがZIP索引を解析し、安全検査後に必要資源をストリーム展開する。Mainはエントリー記述を再検証し、専用領域へのhandle単位の書込みと実バイト数の上限監視だけを担当する。ZIP/XMLパーサーをMainへ持ち込まない。
|
||||
|
||||
最初に入口HTMLを、その後はWebEngineから要求されたCSS、画像、font等を展開する。CRCと上限の検査を終えた資源だけを確定して公開する。展開中の不完全ファイルを描画エンジンへ渡さない。全アーカイブを一括でメモリーへ展開せず、内側のZIPも自動展開しない。
|
||||
|
||||
### 4.3 URLと依存資源
|
||||
|
||||
資源URLは `doc://<session-token>/<relative-path>` とする。`QWebEngineUrlScheme::Syntax::Host` を使い、文書ごとのhostをoriginとする。URLは当該文書のResourceBrokerだけが解決できる。
|
||||
|
||||
| 入力 | 解決規則 |
|
||||
|---|---|
|
||||
| HTMLのsrc/href/srcset | 当該HTMLのURLを基準 |
|
||||
| CSSの `url()` / `@import` | 当該CSSのURLを基準 |
|
||||
| font-face、SVG資源 | 対応する資源の基準URLと同じ規則 |
|
||||
| `#fragment` | 現在の資源内の移動先 |
|
||||
| 相対URLの `..` | URL解決後にroot内なら許可 |
|
||||
| query | URL情報として保持し、OSファイル名へ直接連結しない |
|
||||
| 別origin、file URL、UNC、未知のスキーム | 読込みを拒否 |
|
||||
|
||||
percent-encodingは一箇所で一度だけ復号する。符号化された区切り・NUL・二重復号に依存するパスを拒否し、ZIPエントリー名の `%` を索引作成時にURL復号しない。HTMLの `base` は同一文書root内に解決できるものだけ許可する。外部baseはCSPと資源gateで拒否し、影響する依存資源の欠落を通知する。黙って別の場所へ読み替えない。
|
||||
|
||||
HTML、CSS、SVGの解析はQt WebEngineの描画側で行う。Mainの資源ハンドラーは内容を書き換えるためのHTML/CSSパーサーを持たない。文書タイトルや目次ラベルをアプリUIへ渡す際は文字列として表示し、HTMLとして再解釈しない。
|
||||
|
||||
### 4.4 本文の静的閲覧
|
||||
|
||||
文書JavaScript、form送信、meta refreshによる自動移動、外部iframe、外部画像・CSS・fontを無効にする。ローカルの許可資源は表示する。欠落画像・fontは代替表示、CSS欠落は本文を維持した警告とする。制限の詳細は文書情報から確認できる。
|
||||
|
||||
一般のHTMLメニューバーを自動で目次と決め付けない。`role=doc-toc` 等の意味が明確なnavを目次として読み、それ以外はh1-h6と有効なARIA見出しから「見出し一覧」を作る。見出しがないときは情報なしを示す。文書にIDを恒久追加せず、索引側に一時識別子を持つ。
|
||||
|
||||
## 5. EPUB
|
||||
|
||||
### 5.1 packageと読書順
|
||||
|
||||
ZIPの安全検査後、ArchiveWorkerが `mimetype`、`META-INF/container.xml`、OPF、manifest、spineを解析する。XMLは非検証パーサーとし、外部DTD・外部実体・実体の膨張を禁止する。先頭rootfileを既定packageとし、他のrenditionの存在もメタデータに記録する。
|
||||
|
||||
通常の次/前移動は `linear != no` のspine順で行う。`linear=no` は本文リンクや目次から到達でき、ジャンプ履歴で元へ戻れる。manifestの未知媒体には定義済みfallbackを辿り、循環参照と深さを制限する。利用可能なfallbackがなければ該当章をエラーにし、他の章へ移動可能にする。
|
||||
|
||||
### 5.2 目次・章・ページの区別
|
||||
|
||||
| 優先順/情報 | 使用方法 |
|
||||
|---|---|
|
||||
| EPUB 3 navのtoc | 著者の目次として使用 |
|
||||
| EPUB 2 NCX、またはEPUB 3 nav破損時のNCX | 目次として使用。破損fallback時は通知 |
|
||||
| spineから生成する章一覧 | 目次がない場合の補助。「章一覧」と明示 |
|
||||
| XHTMLの見出し | 見出しジャンプ用。章を開く際に段階的索引化 |
|
||||
| navのpage-list | 出版物のページラベルとして保持 |
|
||||
| landmarks | 補助ナビゲーションとして保持 |
|
||||
|
||||
著者のページラベル、固定レイアウトのページ、リフロー後の画面単位は異なる。リフロー表示では章内進捗を標準とし、画面番号を本の確定ページ番号として保存しない。navとspineの順序が違っても書き換えず、目次操作はnav、通常読書はspineに従う。
|
||||
|
||||
### 5.3 縦書き・RTL・固定レイアウト
|
||||
|
||||
CSSの `writing-mode`、`text-orientation`、`dir`、rubyを尊重する。`page-progression-direction` はページの進行方向であり、本文の文字方向と別に扱う。「次の読書位置」は論理方向で進め、h/j/k/lによる物理スクロールと分ける。
|
||||
|
||||
リフロー型は表示幅・文字サイズ変更後に再レイアウトし、保存した論理位置へ戻す。固定レイアウト型はXHTML viewportまたはSVG viewBoxを基準に拡縮し、本文fontサイズや行幅を強制しない。spine一項目を一ページとして扱い、見開き・RTL・項目ごとのrendition上書きを反映する。混在書籍は章の切替時に表示方式も切り替える。
|
||||
|
||||
### 5.4 位置の復元
|
||||
|
||||
保存情報はDocumentIdentityの内容識別情報と `packagePath / spineIdref / href / fragment / cfi / progression / textQuote` を基礎とする。対象packageとspineを確定した後の復元順はCFI、ID/fragment、textQuote、章内progression。CFIが壊れていても他の情報から近似復元し、近似であることを通知する。文書fingerprintが変わった場合は一致を確認してから古い位置を使う。
|
||||
|
||||
CFI解決と見出し抽出は、原文のDOM構造を基準にする。読書補助の要素を本文DOMへ挿入してCFIの番地を変えない。アプリが管理するUIはQt Quick側へ置く。fontや画像の読込みで位置が動く場合は、レイアウト確定後に同じ論理位置へ再配置する。縦書き・RTLのprogressionは論理読書方向へ正規化する。
|
||||
|
||||
### 5.5 保護と破損
|
||||
|
||||
`encryption.xml` があるだけでDRMと断定しない。IDPFの標準font難読化を認識して描画用に復元する。それ以外の暗号化方式、保護された本文、パスワード付きZIPは初期版非対応とし、`E_DRM_UNSUPPORTED` または `E_ARCHIVE_ENCRYPTED` を返す。
|
||||
|
||||
container/OPFが読めず読書順を確定できないときは開かない。nav破損はNCX・章一覧へfallbackする。spineの一部欠落は欠落章を明示して他の章を読めるようにする。正式な解析に成功していないEPUBを、ZIP内の最初のHTMLを開くだけで成功扱いにしない。
|
||||
|
||||
## 6. Qt WebEngineによる制御
|
||||
|
||||
`JavascriptEnabled=false` でMainWorldの文書スクリプトを停止する。アプリ所有の固定コードは `ApplicationWorld` に置き、DOMの見出し取得、CFI解決、表示位置測定、スクロールを行う。Qtのworld分離ではDOMへアクセスできる一方、JavaScript変数はworld間で共有されない。[QWebEngineSettings](https://doc.qt.io/qt-6/qwebenginesettings.html)、[QWebEngineScript](https://doc.qt.io/qt-6/qwebenginescript.html)
|
||||
|
||||
Qt Quickの `WebEngineView.runJavaScript(script, worldId, callback)` でworldを必ず指定する。文書文字列をコードへ連結せず、検証済みの型付きデータとして渡す。非同期callbackには要求ID・文書世代番号を関連付け、古い結果を捨てる。MainWorld無効化とApplicationWorld実行の組合せは、採用するQt版のG0検証で確認する。[WebEngineView](https://doc.qt.io/qt-6/qml-qtwebengine-webengineview.html)
|
||||
|
||||
本文WebEngineViewには汎用QWebChannelやOS APIを公開しない。設定ファイルから任意JavaScriptを実行する機能も初期版には設けない。位置・見出しの測定結果も未信頼データとしてMainで検査する。
|
||||
|
||||
## 7. 参照資料
|
||||
|
||||
確認日: 2026-09-19。規格の要点を基に、DocView独自の静的閲覧プロファイル・制限・回復動作を設計した。
|
||||
|
||||
- [EPUB 3.3](https://www.w3.org/TR/epub-33/) - package、spine、nav、rendition、font難読化。
|
||||
- [EPUB Reading Systems 3.3](https://www.w3.org/TR/epub-rs-33/) - rootfile選択、file URL、XML処理、script fallback。
|
||||
- [EPUB CFI 1.1](https://idpf.org/epub/linking/cfi/epub-cfi.html) - EPUB内の論理位置の表現。
|
||||
- [QWebEngineUrlScheme](https://doc.qt.io/qt-6/qwebengineurlscheme.html) - Host構文とorigin。
|
||||
- [QWebEngineUrlSchemeHandler](https://doc.qt.io/qt-6/qwebengineurlschemehandler.html) - 独自スキームの資源応答。
|
||||
Reference in New Issue
Block a user