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 * as fs from "fs"
import * as sqlite3 from "sqlite3"
// .clipファイルパス
const path = "../sample_asset/illust1"
const clipPath = path + ".clip"
// Bufferの読み込み
const clipBuffer = fs.readFileSync(clipPath)
// SQLiteのデータは「SQLite format 3」から始まるらしい
const searchText = "SQLite format 3"
// Uint8Arrayに変換
const uint8Array = new Uint8Array(Buffer.from(searchText))
// clipからSQLiteのデータのスタート位置を取得
const findIndex = clipBuffer.indexOf(uint8Array)
// SQLite部分のみ切り出し
const dbBuf = clipBuffer.slice(findIndex, clipBuffer.length)
// 一時書き出し用のSQLiteファイルパス
const dbPath = path + ".sqlite"
// いったんsqliteファイルに書き出し
fs.writeFileSync(dbPath, dbBuf)
// sqlite3モジュールでSQLiteファイルを展開
const db = new sqlite3.Database(dbPath)
db.serialize(() => {
// CanvasPreviewテーブルからImageData(png)を抽出
db.all("SELECT ImageData FROM CanvasPreview", (err, row) => {
if (err) {
console.log(err)
return
}
if (row[0].length > 0) {
// png出力パス
const binaryPath = path + ".png"
// rowのImageDataプロパティからpngを生成
fs.writeFileSync(binaryPath, row[0].ImageData)
}
})
})
解説
clipファイルの読み込み
// .clipファイルパス
const path = "..\\sample_asset\\illust1"
const clipPath = path + ".clip"
// Bufferの読み込み
const clipBuffer = 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」から始まるらしい
const searchText = "SQLite format 3"
// Uint8Arrayに変換
const unit8Array = new Uint8Array(Buffer.from(searchText))
// clipからSQLiteのデータのスタート位置を取得
const findIndex = clipBuffer.indexOf(uint8Array)
// SQLite部分のみ切り出し
const dbBuf = clipBuffer.slice(findIndex, clipBuffer.length)
// 一時書き出し用のSQLiteファイルパス
const dbPath = 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ファイルを展開
const db = new sqlite3.Database(dbPath)
db.serialize(() => {
// CanvasPreviewテーブルからImageData(png)を抽出
db.all("select ImageData from CanvasPreview", (err, row) => {
if (err) {
console.log(err)
return
}
if (row.length > 0) {
// png出力パス
const binaryPath = path + ".png"
// row[0].ImageDataプロパティからpngを生成
fs.writeFileSync(binaryPath, row[0].ImageData)
}
})
})
クエリにてCanvasPreviewからImageDataを取得する。
このとき出力結果のrow
には、次の形でデータが格納されている。
{ ImageData:
<Buffer 89 50 4e 47 0d 0a ... >}
つまり、row[0].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