Skip to content

ayati/yomikake

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

96 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ePub viewer yomikake

ブラウザで動作する、日本語縦書き対応のePubリーダーです。インストール不要で、HTMLファイルを開くだけで使用できます。

ファイル構成とブラウザ対応

ファイル 対応環境 動作サンプル
yomikake.html Windows/Android/macOS の Chrome・Edge・Firefox・Safari https://www.ayati.com/book/yomikake.html
yomikake_ios.html iPhone・iPad の Safari 専用 https://www.ayati.com/book/yomikake_ios.html

iOS(iPhone・iPad)をお使いの方は yomikake_ios.html をご使用ください。 iOS Safari のスクロール挙動の制約により、通常版では正しく動作しません。

特徴

  • 縦書き(vertical-rl) ネイティブ対応
  • 書字方向の切り替え:縦書き・横書き・ePub指定(パブリッシャー任せ)の3モード
  • .epub / .kepub(Kobo) 形式に対応(リフロー型・固定レイアウト型自動判別)
  • マンガ・雑誌モード(FXL / Fixed Layout):pre-paginated 指定の ePub を自動検出し、画像 1 枚 / ページを画面内フィット表示。横長画面では2ページ見開き自動切替(自動 / 単ページ / 見開き設定可)
  • コマ読みズーム(FXL 専用):4 種のズームモードから選択。ストーリーまんが / 4コマまんが(1ページを2×3=6領域に分割してコマ順遷移、LTR書籍は自動で左右反転)/縦合わせ・横スクロール(v1.8.4:縦持ちスマホ×縦書き紙本スキャンに最適。画像を画面の縦いっぱいに拡大、横方向ステップ)/横合わせ・縦スクロール(v1.8.5:横倒しスマホ×横書き紙本スキャンに最適。画像を画面の横いっぱいに拡大、縦方向ステップ)。ダブルタップ/ドラッグ PAN/ラバーバンドで自然な操作感
  • ページジャンプパネル:サイドバー上部に常時表示のジャンプ UI(スライダー・表紙/末尾/±10 ボタン・ページ番号直接入力)。FXL 本・リフロー本どちらでも使え、スマホでも簡単に表紙・末尾・任意ページへ遷移可能
  • ドラッグ&ドロップでファイルを開く(yomikake.html のみ)
  • フォント選択:22種類(游明朝・ゴシック・Google Fonts 各種など)をドロップダウンでプレビュー付き選択。file:// 環境ではウェブフォントが利用不可の場合あり
  • テーマ:8色(標準・セピア・白紙・ダーク・さくら・星空・抹茶・月夜)
  • 文字サイズ・行間・テーマ・余白・次へボタンのサイズのカスタマイズ(設定は自動保存)
  • 設定ポップオーバー:ツールバーのボタンから開くフローティングパネルで全設定を一元管理
  • しおり機能:前回読んだ章・スクロール位置を自動保存・復元
  • しおりエクスポート/インポート:全冊分のしおりをJSONファイルで書き出し・読み込み(デバイス間の引き継ぎに対応)
  • Google Drive しおり同期:Google Drive にしおりを保存・読み込みして複数デバイス間で同期(両ファイル対応、HTTPサーバー経由かつ Client ID 設定が必要)
  • Google Drive しおり自動保存:設定パネルでオンにすると、読み進めるたびに自動でDriveに保存(最小間隔1分)。アクセストークンを有効期限5分前にバックグラウンドで自動更新するため、長時間の読書でも途切れない
  • しおり読み込み後の自動ジャンプ:Drive・ローカルインポートでしおりを上書きした際、現在開いている本の新しい位置が現在より先で最終章でなければ自動でジャンプしてトースト通知
  • ジャンプ履歴しおり:目次クリック・プログレスバークリック等でジャンプする直前の位置を最大4件、本を開いた時の元のしおり位置を1件、TOCサイドバー上部に自動記録。奥付や冒頭を確認してから元のページに戻る、登場人物の初出を確認してから続きに戻るといった用途に活用できる
  • 読みかけリスト:ウェルカム画面に「読みかけ」の本を一覧表示。表紙サムネイル・著者名・進捗バー・最終閲覧日付きのカードから次に読む本を選択できる。File System Access API(Chrome/Edge)と IndexedDB の ePub キャッシュ(両ファイル、直近 3 冊 LRU)により、ファイルピッカーなしで直接再開可能
  • オフライン読書(v1.9.0):直近に開いた ePub 本体(最大 3 冊)をブラウザの IndexedDB に自動保存し、ネット接続が無い環境(航海中・機内など)でも続きから読書可能。両ファイルとも JSZip を HTML に同梱したため、CDN へ接続できないオフライン/file:// 環境でも ePub を展開できる。読みかけリストでオフライン保存済みの本には 「✈ オフラインOK」バッジを表示。設定パネルの **「📂 ePub キャッシュ」**でキャッシュ件数・概算容量の確認とクリアが可能(読書位置・しおりは保持)。※同一オリジン(オンライン時と同じ URL)での再オープンが前提
  • 読みかけリストの編集・削除:リスト右上の「編集」ボタンで編集モードに切り替え、各カードの × ボタンで不要な本をリストから削除可能
  • 本を閉じる:表示設定パネル末尾から現在の本を閉じて読みかけリストに戻る。閉じる際にしおりを自動保存
  • 読了バナー:最終章の末尾に到達すると画面下部にバナーが現れ、そのまま本を閉じてリストに戻るか読み続けるかを選択できる
  • 全文検索:サイドバーの「検索」タブでキーワード検索。章を順に非同期スキャンし、章タイトルと一致箇所スニペットをリアルタイム表示。クリックでジャンプ・ジャンプ履歴に記録。検索中は進捗バー+中止ボタンあり
  • 目次(TOC)サイドバー(目次タブ・検索タブの2タブ構成)
  • 書誌情報表示:ヘルプダイアログにタイトル・著者・チャプター数・目次項目数を表示
  • 4言語対応(i18n):日本語・英語・繁体字中国語・簡体字中国語(ブラウザ言語自動検出、手動切替可)
  • キーボードショートカット(Bluetooth キーボード含む両ファイル対応)
  • ツールバーのマウスホイールスクロール:PC でツールバー上でホイールを回すと横スクロール(yomikake.html のみ)
  • 外部サーバー不要・完全オフライン対応(JSZip を同梱しているため、ネット接続なし・file:// でも動作)

使い方

ファイルを開く

ブラウザに合ったHTMLファイルを開き、以下のいずれかの方法でePubを読み込みます。

  • ツールバーの 「📂 ePubを開く」 ボタンをクリック
  • ePubファイルを画面にドラッグ&ドロップ(yomikake.html のみ)

読みかけの本がある場合、ウェルカム画面に一覧が表示されます。カード右下の 「このファイルを開く」 ボタンを押してファイルを選ぶと前回の続きから自動再開します。読みかけの本がない場合はウェルカム画面のみ表示されます。

操作方法

ページスクロール

操作 yomikake.html yomikake_ios.html
キーボード スペース 1画面分 前進 1画面分 前進
キーボード Shift + スペース 1画面分 後退 1画面分 後退
キーボード / PageDown 前進スクロール 前進スクロール
キーボード / PageUp 後退スクロール 後退スクロール
画面下ボタン「次へ↓」 前進スクロール 前進スクロール
画面上ボタン「↑前へ」 後退スクロール 後退スクロール
左右スワイプ 前進 / 後退スクロール

章末尾に近づくと最後のスクロールで本文の右側に余白ページが表示され、目が左端まで戻らなくてもそのまま次の章に進めます。章先頭 / 末尾に達すると自動的に前の章 / 次の章へ移動します。

章切り替え(リフロー型ePubのみ)

操作 yomikake.html yomikake_ios.html
キーボード 次の章 次の章
キーボード 前の章 前の章
キーボード Home 最初の章 最初の章
キーボード End 最後の章 最後の章
画面右の ボタン 前の章 前の章
画面左の ボタン 次の章 次の章

縦書きの読み方向について: 縦書きは右から左へ読み進めます。そのため「次の章」が キー、「前の章」が キーになっています。

スクロール・章切り替えボタンは通常ほぼ透明で、マウスオーバー時に表示されます。FXL(マンガ・雑誌)モードではチャプターボタンは非表示になり、代わりにサイドバーのジャンプパネルを使います。

ページジャンプ(サイドバー最上部)

ツールバーの 「☰ 目次」 ボタンでサイドバーを開くと、「目次」タブ最上部に「ジャンプ」パネル が常時表示されます。目次が空の FXL 本(マンガ・雑誌)でもこのパネルで任意ページへ移動できます。

要素 動作
スライダー ドラッグ中につまみ上に「現在位置 / 全ページ」バブルが追従。離すとその位置にジャンプ
📖 表紙 先頭ページ(spine[0])へ
-10 / +10 現在位置から 10 ページ前/後へ(マンガの巻頭・章境目への高速移動に便利)
🏁 末尾 末尾ページへ
ページ番号入力 数値を入力して Go または Enter で直接ジャンプ(スマホでは数値キーボードが自動表示)
  • ラベルは FXL 本では「ページ番号」、リフロー本では「章番号」に自動切替
  • ジャンプ前の位置は自動的にジャンプ履歴(↩)に記録され、タップすれば元の位置に戻れます
  • ジャンプ後もサイドバーは開いたまま維持(連続ジャンプ可能)

全文検索

ツールバーの 「☰ 目次」 ボタンでサイドバーを開き、「検索」タブを選択するとキーワード検索が使えます。

  • 入力欄にキーワードを入力して Enter または「検索」ボタンで開始
  • 章を先頭から順に非同期スキャンし、ヒットした章を随時表示(進捗バー付き)
  • 結果には章タイトル・マッチ件数バッジ・前後文脈のスニペットを表示
  • 結果をクリックするとその章に移動(ジャンプ履歴にも記録)
  • 「中止」ボタンで途中停止可能。新しいキーワードを入力すると自動的に前の検索を中断

目次・ジャンプ履歴

ツールバーの 「☰ 目次」 ボタンでサイドバーを開閉できます。「目次」タブで目次項目をクリックすると該当章へジャンプします。しおりから続きを再開した場合はサイドバーが自動的に閉じた状態で起動します。

サイドバー上部には ジャンプ履歴 が自動表示されます(ePubを開いている間のみ有効、localStorage への保存なし):

表示 内容
📌 本を開いたときの保存済みしおり位置(保存位置がある場合のみ表示)
目次クリック・プログレスバークリック等でジャンプする直前の位置(最大4件・新しい順)

クリックするとその位置に移動できます。同じ位置はまとめて1件として扱われます。

読みかけリスト

本を開いていない状態では、ウェルカム画面に 読みかけの本の一覧 が表示されます。

各カードには以下の情報が表示されます。

表示項目 内容
表紙サムネイル ePubの表紙画像(取得できない場合は本のアイコン)
タイトル・著者名 OPFメタデータから自動取得
進捗バー 現在の章 / 全章数と読了率(%)
最終閲覧日 本日・昨日・N日前・月/日

カード右下の 「このファイルを開く」 ボタンを押してファイルを選ぶと、前回の続きから自動再開します。Chrome/Edge(yomikake.html)では File System Access API により、カードに 「このファイルを開く(直接)」 ボタンが表示され、ファイルピッカーを経由せずに直接再開できます。

iOS(yomikake_ios.html)の ePub キャッシュ(v1.8.8):iOS Safari は File System Access API を使えませんが、代替として開いた ePub 本体を IndexedDB にキャッシュし、次回はカードの 「📂 続きから(直接)」 ボタンでファイルピッカーを経由せずに再開できます。直近 3 冊までを LRU で保持。Safari がストレージを破棄した場合は通常のピッカー経路に自動でフォールバックします。設定パネル末尾の 「📂 ePub キャッシュ」 で件数とサイズを確認でき、「クリア」で全削除できます(読書位置・しおりは保持されます)。ホーム画面に追加(PWA)すると保持期間がさらに延びます。

リストから削除:ウェルカム画面の読みかけリスト右上にある 「編集」 ボタンを押すと編集モードになります。各カードに × ボタンが表示され、タップすると確認ダイアログが表示されます。他の環境で読み終わった本や不要になった本をリストから除外できます。フッターの 「完了」 または右上の 「編集」 ボタンで通常モードに戻ります。

途中で本を閉じる:歯車アイコン(設定パネル)の末尾にある 「本を閉じる」 ボタンでいつでも本を閉じてリストに戻れます。閉じる際に読み位置が自動保存されます。

読了バナー:最終章の末尾まで読み進めると、画面下部に読了バナーが表示されます。

  • 「読みかけリストへ」 ボタン:本を閉じてリストへ戻ります。読了済みとしてリストから自動的に除外されます
  • 「閉じない」 ボタン:バナーを閉じてそのまま読み続けられます

リストへの表示条件:最終章を 90% 以上読んだ本は「完読済み」と判定されリストから除外されます。読了バナーで「閉じない」を選んで途中でブラウザを閉じた場合でも、次回起動時には読みかけとして表示されません。

しおりのエクスポート/インポート・Drive同期

ツールバーに4つのしおり操作アイコンがあります。

アイコン 機能 用途
📥 ローカル読込 PCに保存した .json ファイルからしおりを読み込む 手動でのバックアップ復元
📤 ローカル書出 全冊分のしおりを .json ファイルとしてダウンロード バックアップ・手動での引き継ぎ
☁↓ Drive読込 Google Driveからしおりを読み込む(ローカルを上書き) 複数デバイス間の同期
☁↑ Drive保存 しおりをGoogle Driveに保存する 複数デバイス間の同期

ローカル読込・Drive読込でしおりを上書きした際、現在開いている本の新しいしおり位置が現在位置より先で最終章でない場合は、自動でその位置にジャンプしてトーストで通知します。

Drive ボタンと自動保存設定は file:// で直接開いた場合は非表示になります(Google OAuth の制約)。両ファイルで対応しています。

Drive自動保存

設定パネル(⚙アイコン)の Google Drive グループにある「しおり自動保存」をオンにすると、読み進めるたびに自動でDriveに保存されます。

  • オンにする際にDriveへのアクセス確認が行われます
  • Drive保存の発火間隔は最小1分(連続スクロールをまとめて1回に束ねます)
  • アクセストークン(有効期限1時間)は期限5分前にバックグラウンドで自動更新されるため、長時間の読書でも保存が途切れません
  • Googleアカウントのセッション自体が切れた場合のみ自動保存が停止し、トーストで通知します

Drive 機能のセットアップ:

  1. Google Cloud Console でプロジェクトを作成し、Drive API を有効化
  2. OAuth クライアントID を作成(種類: ウェブ アプリケーション)
  3. 承認済みの JavaScript 生成元に使用するオリジン(例: http://localhost:8080https://example.com)を追加
  4. 各ファイル冒頭の GOOGLE_CLIENT_ID 定数に Client ID を貼り付け
  5. HTTP/HTTPS サーバー経由で開く(ローカルの場合: python3 -m http.server 8080

Drive 上のしおりファイル(epub_bookmarks.json)は Google Drive の隠し領域(appDataFolder)に保存されるため、Drive の通常のファイル一覧には表示されません。削除はGoogleアカウント設定の「接続済みアプリ」から行えます。

しおりの識別について:しおりはファイルパスではなく、OPFメタデータのタイトルとチャプター数をキーとして保存されます。ファイルを別フォルダに移動・リネームしても同じしおりが使用されます。

マンガ・雑誌モード(固定レイアウト ePub / FXL)

rendition:layout="pre-paginated" を持つ ePub(マンガ・雑誌・ガイドブック等の固定レイアウト本)を開くと、自動的に FXL モード に切り替わります。リフロー型とはコードパスが完全分離されており、画像 1 枚 / ページ をそのまま画面内にアスペクト比維持でフィット表示します。

  • 自動判定rendition:layout=pre-paginated または legacy <meta name="fixed-layout" content="true"> を検出
  • 単ページ / 見開き自動切替:画面が横長(幅 ≥ 高さ × 1.3)なら見開き、縦長なら単ページ(自動 モード時)
  • 見開き配置page-progression-direction(多くは rtl = マンガ)に従い、2ページが画面中央で隣接するよう自動配置
  • ページめくり:スペース・↓・画面下ボタンで次ページ、Shift+スペース・↑・画面上ボタンで前ページ。iOS ではスワイプでもページ送り可能
  • スマホでのページジャンプ:ツールバー「☰」→ サイドバーの ジャンプパネル で表紙・末尾・任意ページへ高速移動
  • メモリ管理:画像は Blob URL として必要分だけ読み込み、前後のペアのみキャッシュ(数百ページ級の雑誌でも軽快動作)

FXL 本では以下の設定/機能は無効化されます(対象外のため):

  • フォント・文字サイズ・行間・組方向・余白・次へボタンサイズ
  • 全文検索(画像には文字情報がないため)
  • チャプターボタン・←/→ キーによる章送り

代わりに設定ポップオーバーに 「見開き表示」 セレクタ(自動 / 単ページ / 見開き)が表示されます。

コマ読みズーム(FXL 専用)

マンガ・雑誌や紙本スキャンの自炊本を小画面で快適に読むためのズーム機能。⚙設定「ズームモード」で 4 種類 から選択できます。

起動方法

  • ツールバーの 🔍 ズームボタン(FXL 本を開いている間だけ表示)
  • 画面を ダブルタップ(タップした位置の領域にそのまま寄ります/6 領域モード時のみ位置反映)
  • キーボード z(デスクトップのみ)

ズームモード一覧

モード 遷移順/挙動 ステップ数 用途
ストーリーまんが ①右上 → ②左上 → ③右中 → ④左中 → ⑤右下 → ⑥左下 固定 6 通常のマンガ・見開きコマ
4コマまんが ①右上 → ②右中 → ③右下 → ④左上 → ⑤左中 → ⑥左下 固定 6 縦 4 コマレイアウト
縦合わせ・横スクロール (v1.8.4) 画像を画面の縦いっぱいに拡大、Next で書字方向に横ステップ→末尾で次ページ冒頭へ 動的(画像比から算出) 縦持ちスマホ × 縦書き紙本スキャンの自炊小説など
横合わせ・縦スクロール (v1.8.5) 画像を画面の横いっぱいに拡大、Next で上→下にステップ→末尾で次ページ冒頭へ 動的(画像比から算出) 横倒しスマホ × 横書き紙本スキャンの自炊資料・実用書など

LTR 書籍(洋書マンガ・英文雑誌など page-progression-direction="ltr")は 6 領域モードのみ 自動で左右反転されます。⚙設定の「LTR書籍で反転」を OFF にすると反転を無効化できます(軸モードは書字方向ロジックが組み込み)。

軸モード(縦合わせ/横合わせ)について

  • 画像のネイティブ縦横比から自動的に 拡大倍率ステップ数 を算出するため、ページごとに最適なズーム量で表示されます
  • 全画面モード(f キー)と組み合わせると上下/左右の余白が完全消滅し、画面を最大限に活用できます
  • 軸モード ON 中は 見開き表示が自動的に単ページに切り替わります(軸モード OFF で元に戻ります)
  • ドラッグ PAN は軸方向のみ受け付けます(vfill は横方向のみ/hfill は縦方向のみ)
  • ステップ数が動的なので、キーボードの 1-6 直接ジャンプは無効化されます(6 領域モード専用機能)

操作系(全モード共通)

  • Next ボタン(次へ↓):ズーム中は「次の領域」、最終領域のときは「次ページの 1 領域目」
  • Back ボタン(↑前へ):対称に「前の領域」または「前ページの最終領域」
  • ボタンの端に バッジ2/6, など)が表示され、次タップでの動作が一目で分かります
  • ドラッグ:ページ上で指やマウスをドラッグすると 自由 PAN モード(🌀) に移行。プリセット位置に縛られず自由に画面内を移動できます(軸モード時は軸方向のみ)
  • 領域ピル(右下の丸いボタン):モード別にアイコンが変化
    • 6 領域モード:🎯 3/6 表示(プリセット中)/🌀 自由 表示(自由 PAN 中)
    • 縦合わせ:↔ N/total(横ステップを視覚化)
    • 横合わせ:↕ N/total(縦ステップを視覚化)
    • タップで次領域へ/自由 PAN からのスナップ復帰
  • ラバーバンド:ズーム中に画面端からさらに読み進み方向へ 50px 以上引っ張ると、次/前ページに一気にジャンプ。軸モードでは軸方向のはみ出しのみで判定(vfill は横、hfill は縦)

キーボードショートカット(デスクトップ)

キー 動作
z ズーム ON / OFF
16 指定番号の領域にジャンプ(6 領域モード専用)
Space / / PageDown 次領域へ、最終領域なら次ページへ
Shift+Space / / PageUp 前領域へ、先頭領域なら前ページへ
0 / Esc ズーム解除(fit 表示へ戻る)

設定項目(⚙ 設定 → レイアウト)

項目 選択肢 既定値 対象
ズームモード ストーリーまんが / 4コマまんが / 縦合わせ・横スクロール / 横合わせ・縦スクロール ストーリーまんが 全 FXL
拡大倍率 1.5x / 2.0x / 2.5x 2.0x 6 領域モード専用(軸モードは画像比から自動算出)
LTR書籍で反転 ON / OFF ON 6 領域モード専用

見開き時の挙動(6 領域モードのみ):見開き表示中も 6 領域は「片側ページ単位」で定義されます(= 見開き全体では 12 領域)。現在フォーカス中のページ(state.currentSpineIdx が指す側)の 6 領域を順次たどり、6 領域目の Next でもう一方のページに自動切替、そのページの 6 領域終了で次ペアへ進みます。

永続化ポリシー:ズームの ON/OFF 状態・現在の領域位置・PAN オフセット・倍率の動的算出値は localStorage には保存しません(本を開くたびにズーム OFF で起動)。ズームモード拡大倍率LTR 反転 の 3 つの設定のみ epub_settings に永続化されます。

表示設定

ツールバー設定ポップオーバーで以下の設定を変更できます。変更は即時反映され、次回起動時に自動復元されます。

設定 選択肢 対象
フォント 22種:パブリッシャー / 明朝体 / ゴシック / Google Fonts(Noto Serif JP・Shippori Mincho・Klee One・Hina Mincho 他)/ 中文・欧文フォントなど リフロー型
文字サイズ A- / A+ボタンで 60〜400%(10%刻み、200%超は20%刻み) リフロー型
行間 狭い(1.6) / 標準(2.0) / 広い(2.4) / 最広(2.8) リフロー型
組方向 縦書き(強制 vertical-rl) / 横書き(強制 horizontal-tb) / ePub指定(ePubのCSS優先) リフロー型
余白 標準 / 中 / 狭 / なし リフロー型
次へボタン 小(デフォルト) / 中 / 大(画面下半分に近い大きさ) ※デバイスの持ち方に合わせて調整 リフロー型
見開き表示 自動(アスペクト比で判定) / 単ページ / 見開き FXL
ズームモード(コマ読みズーム) ストーリーまんが / 4コマまんが / 縦合わせ・横スクロール / 横合わせ・縦スクロール FXL
拡大倍率(コマ読みズーム) 1.5x / 2.0x / 2.5x(6 領域モード専用) FXL
LTR書籍で反転(コマ読みズーム) ON / OFF(6 領域モード専用) FXL
テーマ 標準 / セピア / 白紙 / ダーク / さくら / 星空 / 抹茶 / 月夜(8色) 共通

テーマはePub本文エリアの背景色・文字色に直接反映されます。FXL 本でもテーマの背景色はページ外余白に反映されます。フォントにGoogle Fontsを選択した場合、file:// 環境ではウェブフォントが読み込めない場合があります。

技術仕様

動作環境

ファイル 動作ブラウザ
yomikake.html Chrome・Firefox・Edge(Windows/macOS/Android)、Safari(macOS)
yomikake_ios.html Safari(iPhone・iPad)専用

外部ライブラリ: JSZip 3.10.1(両ファイルにインライン同梱。取り込み時に SRI ハッシュで検証済み。実行時のCDN取得が無いため、オフライン/file:// でも ePub を展開可能)、Google Identity Services(Drive同期時のみ CDN から読み込み、SRI非対応) ビルド不要・インストール不要

アーキテクチャ概要

両ファイルとも単一HTMLファイル構成で、基本的なアーキテクチャは共通です。

yomikake.html / yomikake_ios.html(単一ファイル)
│
├── CSS               テーマ変数(--bg / --ui-bg / --accent 等、UI部分のみ)
├── HTML              ツールバー・サイドバー・iframe・ステータスバー
└── JavaScript
    ├── state             中央状態管理(spine / manifest / toc / 表示設定)
    ├── THEME_CONTENT     テーマ別 ePub 本文色マップ
    ├── loadEpub()        ZIP解析 → OPF読み込み → spine/TOC構築
    ├── buildSrcdoc()     リソースインライン化 → CSS注入 → srcdoc生成
    ├── buildScrollScript() iframe内スクロール制御・位置報告スクリプト生成
    ├── renderPage()      章レンダリング → iframe.srcdoc に設定
    ├── I18N / t() / initLang() / setLang() / applyI18n()  多言語対応
    ├── saveSettings() / loadSettings()                   表示設定の永続化
    ├── savePos() / loadSavedPos()                        しおり位置の永続化
    ├── exportBookmarks() / collectBookmarks() / (import handler)  しおりJSON書き出し・読み込み
    ├── pushJumpHistory() / updateJumpHistoryUI() / labelForPos()       ジャンプ履歴しおり
    ├── switchSidebarTab()                                サイドバータブ切り替え(目次/検索)
    ├── startSearch() / runSearch() / htmlToText()        全文検索(非同期ストリーミング)
    ├── findMatchSnippets() / appendSearchResult()        検索結果スニペット生成・表示
    ├── loadPreviewFonts() / buildFontPickerList()        フォントピッカー(22種プレビュー付き)
    ├── driveAuth() / scheduleTokenRefresh()                            OAuth トークン取得・自動更新
    ├── driveFindFile() / driveUpload() / driveDownload()              Google Drive同期
    ├── buildReadingList() / formatRelativeDate()         読みかけリスト構築・日付フォーマット
    ├── extractCoverThumb() / saveBookMeta()              表紙サムネイル抽出・メタデータ保存
    ├── closeBook()                                       本を閉じてリストへ戻る
    ├── showFinishedBanner() / dismissFinishedBanner()    読了バナー表示・非表示
    ├── buildFxlPairs() / findPairIdxForSpine()           FXL: 見開きペア構築・位置検索
    ├── extractFxlImagePath() / loadFxlPageBlobUrl()      FXL: 画像抽出・Blob URL 生成
    ├── renderFxlPair() / advanceFxlPage()                FXL: ペア描画・ページ送り
    └── updateJumpPanel() / jumpToSpineIdx() / jumpToCover() / jumpToEnd() / jumpRelative() / jumpToInput()
                                                           ジャンプパネル UI と操作

FXL(Fixed Layout)モードはリフロー型とコードパスを完全分離しています:

  • iframe を使わず親ウィンドウに <img> を直接配置(#fxl-container / #fxl-spread / #fxl-page-a,b
  • rendition:layout="pre-paginated" を loadEpub() で検出して state.renderMode='fxl' にセット、body.mode-fxl クラスで UI 全体を連動切替
  • 画像は URL.createObjectURL(blob) で生成、trimFxlBlobCache() で前後ペア分のみ保持(メモリ節約)
  • buildFxlPairs()<itemref properties="page-spread-right/left/rendition:page-spread-center">page-progression-direction から論理ペア配列を生成
  • 単ページ / 見開きは isEffectiveSpread() がアスペクト比またはユーザー設定で判定、#fxl-spread.spread/single クラスと CSS object-position で画像を中央隣接配置

iOS版の実装上の違い(yomikake_ios.html)

iOS Safari の iframe 内では scrollLeft 代入・window.scrollTo が正常動作しないため、スクロールを CSS transform で実装しています。

  • body { position:fixed; writing-mode:vertical-rl; width:max-content; will-change:transform } で縦書き列を body 全体に展開
  • body.style.transform = translateX(tx) で列をスライドさせてページ送りを実現
  • scroll API を一切使用しないため iOS Safari の挙動に依存しない
  • iPad での描画初期化タイミング問題を double-rAF + 500ms フォールバックで解決
  • ドラッグ&ドロップは非対応(iOS Safari の制約)
  • File System Access API が使えないため、代替として IndexedDB に ePub Blob をキャッシュepub_viewer_files DB / 直近 3 冊 LRU)。読みかけリストから「📂 続きから(直接)」でピッカー無し再開を実現。navigator.storage.persist() でホーム画面追加時の長期保持を要求し、QuotaExceededError 時は最古エントリ削除+1 回リトライ。失敗時は通常のピッカー経路へ silent fallback

ePub解析フロー

  1. JSZipでZIPを解凍
  2. META-INF/container.xml → OPFファイルのパスを取得
  3. OPFから manifest(リソース一覧)と spine(読み順)を構築
  4. nav要素から目次(TOC)を構築
  5. renderPage() で各章を描画

リソース解決とiframe描画

外部CSSの url() 参照・画像・フォントをすべてbase64データURIに変換し、iframe.srcdoc に直接埋め込みます。これにより blob URL を使用せず、同一オリジン問題を回避しています。

buildSrcdoc() では描画前に以下のセキュリティ処理を行います:

  1. ePub 内の <script> 要素を除去(XSS対策)
  2. ePub 由来の <base> 要素を除去し <base href="about:blank"> を挿入(file:// 環境で未解決相対URLが親ページURLに解決されるのを防止)

テーマと本文色

テーマ選択はUI(ツールバー・サイドバー)と ePub 本文エリアの両方に適用されます。本文色は CSS 変数経由ではなく THEME_CONTENT マップで直接管理します。

テーマ 本文背景 本文テキスト
標準 #fdf8f0(温かみのある白) #1a1008(濃いこげ茶)
セピア #dfc89a(やや暗めのクリーム) #2e1a06(深い茶)
白紙 #ffffff(純白) #111111(ほぼ黒)
ダーク #111111(ほぼ黒) #e8e8e8(明るいグレー)
さくら #fff5f7(淡いピンク白) #3d1020(深紅系)
星空 #0e1a2e(深夜の空) #c8daf0(月明かり色)
抹茶 #f0f5e8(薄緑白) #1e301a(深緑)
月夜 #f0f0fa(淡い青白) #1a1840(深夜の紺)

しおり・設定の永続化

すべて localStorage に保存します。file:// URL環境でも動作します。

キー 内容
epub_pos_{タイトル}_{spine数} 章/ページインデックス+スクロール比率(0〜1、FXLは常に0)+著者名・表紙サムネイル(base64)・最終閲覧日時
epub_last_book 最後に読んだ本のタイトルと bookKey
epub_settings フォント・文字サイズ・行間・テーマ・余白・組方向・次へボタンサイズ・見開き表示(FXL用)
epub_lang 選択中の表示言語(ja / en / zh-TW / zh-CN

リフロー型・FXL 型ともに spineIdx ベースで位置を保存するため、読みかけリスト・Drive 同期・しおりエクスポート/インポートはすべて共通のフォーマットで動作します。

スクロール位置はデバウンス500msで保存します。保存に失敗した場合(ストレージ容量不足等)はトースト通知で警告します。

ePub 本体キャッシュ(IndexedDB):オフライン読書のため、直近に開いた ePub 本体(最大 3 冊・LRU)を IndexedDB(epub_viewer_files)に自動保存します。localStorage のしおりとは独立したストレージで、設定パネルの「📂 ePub キャッシュ」からクリアできます(しおりは保持)。起動時に navigator.storage.persist() で永続化を要求します。本体キャッシュはオリジン単位のため、オンライン時に読んだのと同じ URL での再オープンが前提です。

スクロール制御(yomikake.html)

縦書きのスクロールはブラウザ間で scrollLeft の挙動が異なります:

ブラウザ RTL scrollLeft の基準
Chrome 0(右端)から負の方向に増加
Firefox 最大値(右端)から0(左端)に減少

起動時に scrollLeft の符号を確認して判定し、両ブラウザで正しく動作するよう分岐しています。

メッセージ型 方向 意味
EPUB_SCROLL 親 → iframe スクロール指示(direction: 1=前進, -1=後退)
EPUB_EDGE iframe → 親 端まで到達(章切り替えトリガー)
EPUB_POS iframe → 親 現在のスクロール比率(しおり保存用)
EPUB_LINK iframe → 親 ePub内部リンクのクリック(href を親が解決してspine移動)

セキュリティ対策

対策 実装箇所 内容
JSZip 改ざん検知 <script> タグ JSZip 3.10.1 を SRI ハッシュで検証のうえ HTML にインライン同梱(実行時のCDN取得なし)。GIS のみ CDN 読み込み
ePub XSS 防止 buildSrcdoc() ePub 内 <script> 要素を描画前に除去
file:// URL 漏洩防止 buildSrcdoc() <base href="about:blank"> で相対URL解決先を無害化
不正 ePub の明示エラー loadEpub() container.xml / rootfile / OPF の存在を検証し、欠落時は具体的なエラーメッセージで通知
Drive OAuth トークン管理 driveAuth() / scheduleTokenRefresh() アクセストークンはメモリのみ保持(localStorage 非使用)。TokenClient を再利用し prompt:'' でポップアップなしのサイレントリフレッシュを実施。有効期限5分前に自動更新をスケジュール
Drive ファイルID バリデーション driveFindFile() API レスポンスの fileID を /^[a-zA-Z0-9_-]{10,200}$/ で長さ含め検証してから URL に使用
Drive 権限の最小化 OAuth スコープ drive.appdata スコープのみ使用(アプリ専用隠しフォルダのみアクセス可)

既知の制限:

  • ePub 内のインライン on* イベントハンドラは除去しない(対応コストに対しリスクが低いため)
  • postMessage の送信先 origin は file:// 制約により "*" を使用(受信側で e.source === iframe.contentWindow を検証して対応)
  • GIS(Google Identity Services)スクリプトは SRI 非対応(Google 提供 CDN のため同等の信頼レベル)

ライセンス

MIT License © 2026 N.Aono