CLIP STUDIO PAINTファイル(.clip)のサムネイル画像をNode.js上で出力してみました。
CLIPSTUDIO PAINTのファイルからサムネイル画像を取得
CLIP STUDIO PAINTで作成した漫画データのサムネール出力の為の調査
こちらの記事のように、Pythonやシェル、Goで実装されたライブラリ等はあったものの、JavaScript(TypeScript)で実装されたものが無かったので調べてみました。
概要
- clipファイルからSQLite部分のデータを切り出し
- 切り出したSQLiteファイルからサムネイル画像のデータを抽出
次節から実装になりますが、ファイルの読み書きにfs.readFileSync/writeFileSync
を使用しています。
また、単なる出力テストなので、実装する場合はfs.readFile/writeFile
の使用及び適宜例外処理をお願いします。
コード
とりあえず全体像。TypeScriptで記述しています。
外部モジュールとしてsqlite3をインストールしてください。
$ npm i sqlite3
import*asfsfrom"fs"import*assqlite3from"sqlite3"// .clipファイルパスconstpath="../sample_asset/illust1"constclipPath=path+".clip"// Bufferの読み込みconstclipBuffer=fs.readFileSync(clipPath)// SQLiteのデータは「SQLite format 3」から始まるらしいconstsearchText="SQLite format 3"// Uint8Arrayに変換constuint8Array=newUint8Array(Buffer.from(searchText))// clipからSQLiteのデータのスタート位置を取得constfindIndex=clipBuffer.indexOf(uint8Array)// SQLite部分のみ切り出しconstdbBuf=clipBuffer.slice(findIndex,clipBuffer.length)// 一時書き出し用のSQLiteファイルパスconstdbPath=path+".sqlite"// いったんsqliteファイルに書き出しfs.writeFileSync(dbPath,dbBuf)// sqlite3モジュールでSQLiteファイルを展開constdb=newsqlite3.Database(dbPath)db.serialize(()=>{// CanvasPreviewテーブルからImageData(png)を抽出db.get("SELECT ImageData FROM CanvasPreview",(err,row)=>{if(err){console.log(err)return}if(row.length>0){// png出力パスconstbinaryPath=path+".png"// rowのImageDataプロパティからpngを生成fs.writeFileSync(binaryPath,row.ImageData)}})})
解説
clipファイルの読み込み
// .clipファイルパスconstpath="..\\sample_asset\\illust1"constclipPath=path+".clip"// Bufferの読み込みconstclipBuffer=fs.readFileSync(clipPath)
fs.readFileSync
でclipファイルの読み込みを行う。fs.readFileSync
はBuffer
を返す。
SQLiteのデータの位置を探索
clipファイルは何らかのメタデータ+SQLiteで構成されているらしく、
そのままでは下記のとおりSQLiteとして展開できない。
// PowerShellで確認
$ C:\...> sqlite3 illust1.clip
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
// テーブルリストを出力
$ sqlite> .table
Error: file is not a database
そこで、SQLiteのデータだけ切り出してやる必要がある。
噂によるとSQLiteのデータは「SQLite format 3」から始まるらしい。
// SQLiteのデータは「SQLite format 3」から始まるらしいconstsearchText="SQLite format 3"// Uint8Arrayに変換constunit8Array=newUint8Array(Buffer.from(searchText))// clipからSQLiteのデータのスタート位置を取得constfindIndex=clipBuffer.indexOf(uint8Array)// SQLite部分のみ切り出しconstdbBuf=clipBuffer.slice(findIndex,clipBuffer.length)// 一時書き出し用のSQLiteファイルパスconstdbPath=path+".sqlite"// いったんsqliteファイルに書き出しfs.writeFileSync(dbPath,dbBuf)
Buffer#indexOf
にて、SQLiteのインデックスを取得する。
インデックスが分かったら、そこから終点までslice
してやる。
取得したバッファをfs.writeFileSync
でSQLiteファイルとして書き出し。
SQLiteの展開とpngの書き出し
SQLiteファイルのテーブルは次のようになっており、
CanvasPreviewテーブルにプレビュー画像のデータが保持されている。
// PowerShellで確認
$ C\...> sqlite3 illust1.sqlite //←上で生成したやつ
$ sqlite3> .table
AnimationCutBank Layer
Canvas LayerThumbnail
CanvasItem Mipmap
CanvasItemBank MipmapInfo
CanvasPreview Offscreen
ElemScheme ParamScheme
ExternalChunk Project
ExternalTableAndColumnName RemovedExternal
sqlite3モジュールにより、先ほど生成したSQLiteファイルを展開する。
// sqlite3モジュールでSQLiteファイルを展開constdb=newsqlite3.Database(dbPath)db.serialize(()=>{// CanvasPreviewテーブルからImageData(png)を抽出db.get("select ImageData from CanvasPreview",(err,row)=>{if(err){console.log(err)return}if(row.length>0){// png出力パスconstbinaryPath=path+".png"// row.ImageDataプロパティからpngを生成fs.writeFileSync(binaryPath,row.ImageData)}})})
クエリにてCanvasPreviewからImageDataを取得する。
このとき出力結果のrow
には、次の形でデータが格納されている。
{ImageData:<Buffer89504e470d0a...>}
つまり、row.ImageData
で目的のサムネイル画像のデータを取得することが可能。
最後に、fs.writeFileSync
によりillust1.png
にrow.ImageData
を書き出してやれば終了である。
参考
CLIP STUDIO PAINTで作成した漫画データのサムネール出力の為の調査
CLIPSTUDIO PAINTのファイルからサムネイル画像を取得
CLIP STUDIO PAINTの.lipファイルをハックして作業動画を書き出すWindowsアプリを作った
Node.js で TextEncoder や SHA512 を作る
Get list of tables from SQLite in Node.js