Jisui (自炊) 処理 は何をしますか?
スキャン画像 → ZIP / CBZ / PDF への変換ルール。書籍だけでなく、スキャンされたあらゆるもの(雑誌、文書、ノートなど)に適用します。 このスキルは出力ファイルの名前や保存場所を決定しません。呼び出し元(プロジェクトの CLAUDE.md またはユーザーの指示)に従ってください。どちらも指定がない場合はユーザーに確認してください。
指定されたパス
引数: $ARGUMENTS
最初に指定されたパス(スキャンフォルダ、画像ファイル、またはPDF)を読み取ります。何も指定されていない場合は、現在のディレクトリ(.)を使用します。ただし、リクエスト自体でスキャンが指定されている場合は除きます。その後、各パスを再帰的にリストアップし(サブフォルダ、ファイル名、拡張子、数、いくつかのファイルの画像寸法/DPI)、いくつかの画像(最初、最後、および中間のいくつか)を確認します。そこから構造を割り出します(例:1ボリュームにつき1フォルダ、または1フォルダに複数ボリューム、どのファイルが表紙/裏表紙/カバー/本文ページか、奇数/偶数ページ分割、重複や不要ファイル)。そして、具体的なjisui計画(ボリューム、ページ順、表紙/裏表紙/カバーのソース、推奨オプション)を提案します。その計画を以下の質問に折り込み、ユーザーが一度の確認で修正できるようにします。
開始前
呼び出し元がまだ決定していない項目について、ユーザーに質問します(AskUserQuestionを1回)。
- 出力形式:ZIP / CBZ / PDF。
- 表紙(表カバー):配置 — 出力の最初のページ / 使用しない / 出力の隣に別ファイル。ソース — ユーザーが指定するファイル / 最初のスキャン / カバーの表側 / Web上で見つけた画像。
- 裏表紙:配置 — カバーの前の最後のページ / 使用しない / 出力の隣に別ファイル。ソース — ユーザーが指定するファイル / 最後のスキャン(カバーが最後のスキャンの場合はその前)/ カバーの裏側 / Web上で見つけた画像。
- カバー(表紙カバーのスキャンまたは画像):同じ2つの質問。ソース — ユーザーが指定するファイル / 最後のスキャン / Web上で見つけた画像。
スキャンからのソースは、その配置が「使用しない」の場合でも本文ページから除外されます(処理も保持もされません)。表紙/裏表紙/カバーのソースがない場合、すべてのスキャンが本文ページになります。Web画像の場合は、スキャンに一致する版(タイトル、出版社、ISBNが可視ならそれ)を検索し、最大バージョンをスクラッチ領域にダウンロードし、ユーザーに表示して使用前に確認を得ます。
ページ処理
- 表紙 / 裏表紙 / カバー(「開始前」で決定した通り)。以下のすべてのステップは本文ページのみに適用されます。
- 表紙:そのまま使用、加工なし(カラー、寸法変更なし)。含める → 最初のページ。 カバーの表側から:直立したカバーを見て、表パネルの列X0~X1 — 背の折り目と表フラップの折り目の間(左綴じの本では背の右側、右綴じ/縦書きの本では左側)を特定し、その部分のみを切り抜きます(フル高、カラーのまま)。
- 裏表紙:表紙と同じ。含める → 最後の本文ページの後(カバーの前)。カバーの裏側から:裏パネル、裏フラップの折り目と背の折り目の間(表パネルとは背を挟んで反対側)。
- カバー:正しい向きに回転するのみ(カラー、寸法変更なし)。ピクセル単位で正確に回転。JPEGの寸法でロスレス回転ができない場合はPNGを使用。含める → 最後のページ。PDFでは、そのページのみ実寸(DPIに従って)にします。
- 別ファイル:出力の隣に
<出力名>-cover.<ext>/<出力名>-back.<ext>/<出力名>-jacket.pngとして保存。スクリプトが書き込んだものから変更しません。
- 傾き補正:各ページについて、±3°の範囲を0.1°刻みで傾きを検出し補正します。
- 回転(行が傾いている)と剪断(行は水平だがテキストブロックの左端/右端のみが傾いている — 用紙がシートフィーダー内で横に滑る場合に発生)を別々に検出し補正します。
- テキストが少ないページ(タイトルページ、半タイトル、短いページ)は傾きを確実にチェックできないため、補正しません。
- 余白最小化:テキストブロックの各辺に2mm(300dpiで24px)の余白のみを残してトリミングします。
- スキャナーの影、ページ端の細い線、テキストブロック内の小さなゴミを含めないでください。ページ端の影や線は白くします。
- 統一寸法:1ボリューム内の全本文ページは同じ寸法になります。
- ボリュームごとに1つのクロップボックスサイズを決定し、ページごとに配置を調整してそのページのテキストブロックに合わせます(テキストブロックの位置はスキャンごとに数mmずれます)。テキストが少ないページ(タイトルページ、章扉、短いページ)は、同じ面(奇数/偶数)の近くのページの位置を使用するため、元のレイアウトを維持します。
- ボックスからはみ出すページは、収まるように少し縮小します。
- 指示があった場合のみ:見開きイラストやタイトルページなどの画像ページを本文ボックスから外し、本文と同じ高さで幅が広い共有の画像ページボックスを与えます(
--picture)。これにより本文ページの余白を小さくできます。
- 空白ページ:スキップせず、空白ページとして保持します。
- すべてのページを保持:広告を含め、ページを削除しません。
- 白黒:指示があった場合のみ、白黒に二値化します(非可逆のため、デフォルトでは行いません)。
- テキストは二値化しますが、写真やイラストなどの画像領域は二値化せず、グレースケールにします。
- しきい値は200(0~255。これより暗いピクセルは黒になる)を基準とします。自動選択(大津、約160)ではテキストが薄くなりすぎました。
- 処理後、使用したしきい値を報告し、その値が適切かユーザーに確認します。
- OCR:指示があった場合のみ、PDFにOCRテキストレイヤーを追加します(
ocrmypdf -j 2 -l jpn+eng --output-type pdf、画像は変更なし。-lはコンテンツの言語に合わせます。例:韓国語の場合はkor+eng)。Apple OCRはページ数が多いとタイムアウトしやすいため、qpdfで50ページずつに分割し、それぞれOCRし、失敗したものだけを再実行してからマージします。- 繰り返し使用すると、Apple OCR(LiveText)がエラーなしで途中から空のテキストレイヤーを返し始めることがあります。マージ前に各ページを
pdftotextで確認してテキストが抽出されたことを確認します。本文ページが空のチャンクがある場合は、少し待って再実行します。それでも解決しない場合はMacを再起動します。
- 繰り返し使用すると、Apple OCR(LiveText)がエラーなしで途中から空のテキストレイヤーを返し始めることがあります。マージ前に各ページを
- ブックマーク(索引付け):指示があった場合のみ、PDFに目次ブックマーク(アウトライン)を追加します。
- 目次ページを読み取り、目次と同じ階層で部/章/節を追加します。見出しは印刷された通り正確に(序文、あとがき、解説、注釈、索引などが目次にあればそれらも含む)。目次自体のエントリを最初に置き、目次ページを指すようにします。目次にページ番号がない部/章は、そのタイトルページまたは見出しがあるページを指します。
- 印刷されたページ番号とPDFページの対応は、複数の場所でページ番号を読み取って決定します。前付けなどによりずれる可能性があるため、すべてのトップレベルエントリといくつかの下位エントリについて、見出しが実際に対象ページに現れているか確認します。
pikepdfを使用してメタデータ編集と同じ手順で書き込み(画像再圧縮なし)、/PageMode /UseOutlinesを設定します。書き込み後に読み戻して検証します。目次がない場合はブックマークを追加せず、ユーザーに伝えます。
実行
uv run <このスキルのフォルダ>/scripts/jisui.py <画像フォルダ> <出力フォルダ> [--threshold N | --gray | --color] [--no-deskew 1,12,...] [--picture 3,24,...] [--dpi N] [--cover first|FILE|jacket:X0-X1] [--back last|FILE|jacket:X0-X1] [--jacket last|FILE] [--jacket-rotate 0|90|180|270] は、ステップ0(--cover:最初のスキャンまたはFILEをそのまま 0000.<ext> としてコピー、または jacket:X0-X1 で直立したカバーのその列を 0000.png として切り抜く(--jacket が必要)。--back:裏表紙も同様、最後のスキャン(カバーの前にあるもの)から取得し、本文ページの直後に NNNN.<ext>(カバーから切り抜いた場合は .png)として書き込む。--jacket:最後のスキャンまたはFILEを取得し、--jacket-rotate で時計回りに回転して、最後の NNNN.png として書き込む。表紙/裏表紙/カバーのソースがある場合は、その配置に関わらず常にこれらを渡す)、ステップ1~3、ステップ6(白黒)(--gray の場合は白黒化せずページはグレースケールのまま。--color の場合はページはカラーのまま)、ページ画像(PNG)とレポート(<出力フォルダ>-report.json:しきい値、ボックス寸法、空白/縮小ページ、グレースケール領域のあるページ)を実行します。ページ番号は出力番号(NNNN.png。表紙は0、最初の本文ページは1)。傾き補正しないページ番号は --no-deskew に、画像ページ番号は --picture に渡します(レポートには画像ページボックスの寸法も記載されます。高さは本文と同じなので、--imgsize は1つで十分)。作業出力はスクラッチ領域に置きます。元のスキャンやユーザーのフォルダには決して書き込みません。
- トリムサイズを推測しないでください。デフォルトでは余白を最小化し、本文ページのサイズを統一します。mm↔px変換の解像度は画像のDPIタグから取得します。タグがない場合、または150未満(プレースホルダーの72など)の場合は300dpiと仮定します(それ以外の場合は
--dpi Nを渡します)。 - PDFを作成する場合、サイズは
img2pdf --imgsize x<ボックス高さmm>で設定します(出力PNGにはDPI情報がありません)。カバー/カバーページは--imgsizeなしで個別に構築し(DPIタグに従った実寸)、pikepdfで追加します。 - 処理後、いくつかのページを画像として確認し(傾き、トリミング、二値化)、レポート内の空白/縮小ページが妥当であることを確認してから出力を生成します。
出力
パッキングの前に、配置が「使用しない」または「別ファイル」の表紙/裏表紙/カバー画像をページセットから取り出し(別ファイルはステップ0と同様に保存します)。
- ZIP:処理済みページ画像をアーカイブのトップレベルに、ページ順のファイル名で配置します。画像を再圧縮しないでください(
zip -0)。.DS_Storeなどは含めないでください。 - CBZ:ZIPと同じで、拡張子が
.cbz。リーダーは最初の画像をサムネイルとして使用します。ComicInfo.xmlはメタデータ編集でのみ追加します。 - PDF:ZIPと同じ画像を再圧縮せずにレイアウトします(
img2pdf)。OCRはステップ7、ブックマークはステップ8に従います。 - モード名:本文ページによって決定されます —
color/grayscale/bw(カラーの表紙だけではcolorになりません)。OCRレイヤーがある場合は+ocr、ブックマークがある場合は+tocを追加します(ZIP/CBZでは不可)。ファイル名に含めるかどうかは呼び出し元が決定します。
メタデータ編集(タイトル / 著者)
呼び出し元(カタログ、ユーザー)から値を取得します。推測して入力しないでください。
- タイトルと著者フィールドのみ。翻訳者を著者に入れないでください。
- PDF:タイトルと著者をInfo辞書とXMPの両方に、ページを再圧縮せずに書き込みます(
pikepdf)。 - EPUB:OPFのメインの
dc:titleと各dc:creator(およびそれらをrefinesするdcterms:*/file-asメタ)のみを変更します。それ以外は触りません。エントリの順序と圧縮方法を維持してZIPを書き換え、mimetypeを最初に(圧縮なし)配置します。Standard Ebooksのサブタイトル/長いタイトルのエントリはそのままにしておきます。 - CBZ:アーカイブの最後に
ComicInfo.xml(<Title>、<Writer>)をストア(zip -0)で書き込み、画像エントリは変更しません。右から左に読む本(日本語縦書き、マンガ)の場合は、ユーザーの確認を得て<Manga>YesAndRightToLeft</Manga>も追加します。 - ZIP(スキャン画像):メタデータがないためスキップします。
- スクラッチ領域のコピーで作業し、検証(ページ数/エントリが変更されていないこと、読み戻したメタデータが指定された値と一致すること)してから、元のファイルを置き換えます。置き換えはユーザーのファイルを上書きするため、呼び出し元が承認していない限り最初に確認します。