Comipoli では JXA で標準出力から画像データを得る必要がある。
doShellScript が使えるから簡単だね、と思ったら…
こいつだと 1 ライン分(一行目)しか得られないようだ。
てか画像データを直接受け取っても JavaScript にはバイナリのデータ型が無い。
NSData として受け取る必要がある。
JXA で UNIX コマンドを使う方法は cookbook にあるんだけど。
Getting the Application Instance ? JXA-Cookbook/JXA-Cookbook Wiki ? GitHub
NSTask の launch は現在非推奨になっているんだなぁこれが。
NSTask – Foundation | Apple Developer Documentation
Swift では Process.run() という新しい手段ができている。
NSTask Sample for Swift ? GitHub
で、Objective-c, JXA では Process class は使えないと。
つまり Objective-c に NSProcess が追加されるまで launch を使うしかない。
ところで NSTask.launchPath にはフルパスを!と皆書いているけど。
/usr/bin/env を入れればいいと思うのは筆者だけ?
パスが通っていればどこにあってもいいように Python のシバンに使うよね。
てなことで、ちょっと長いけど。
#!/usr/bin/osascript // osacompile -l JavaScript -o Comipoli.app comipoli.js ObjC.import("Cocoa"); const PATH = "/Users/sasakima-nao/Documents/もっと GF.cbz"; const PICEXT = /\.(jpe?g|png|gif)$/i; class ComipoliArchive { constructor() { this.status = 0; this.namelist = []; } getData(arr) { let task = $.NSTask.new; let pipe = $.NSPipe.pipe; task.standardOutput = pipe; task.launchPath = "/usr/bin/env"; // Deprecated task.arguments = arr; task.launch; // Deprecated //task.waitUntilExit; // No! let data = pipe.fileHandleForReading.readDataToEndOfFile; return data; } getString(arr) { let data = this.getData(arr); let str = $.NSString.alloc.initWithDataEncoding(data, $.NSUTF8StringEncoding); return str.js; } newArchive(path, is_unrar, is_7za) { this.path = path; if (/\.(cbz|zip)$/i.test(path)) { this.status = 0; this.namelist = []; let output = this.getString(["unzip", "-Z", path]); let list = output.split("\n"); for (let line of list) { if (line.startsWith("-")) { let name = line.slice(53); if (PICEXT.test(name)) { this.namelist.push(name); } } } this.namelist.sort(); } else { return false; } return true; } _zipEscape(str) { const ESCAPE = "[]*?!^-\\"; let res = ""; for (let s of str) { if (ESCAPE.includes(s)) res += "\\"; res += s; } return res; } getItem(num) { let name = this._zipEscape(this.namelist[num]); let data = this.getData(["unzip", "-pj", this.path, name]); return $.NSImage.alloc.initWithData(data); } getLength() { return this.namelist.length; } } class ComipoliWindow { constructor(app) { ObjC.registerSubclass({ name: "WinDelegate", protocols: ["NSWindowDelegate"], methods: { "windowDidResize:": { types: ["void", ["id"]], implementation: (aNotification)=> { this.fitImage(); } }, "windowWillClose:": { types: ["void", ["id"]], implementation: (notification)=> { app.terminate(0); } } } }); // var this.num = 0; this.window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer( $.NSMakeRect(0, 0, 600, 500), $.NSTitledWindowMask | $.NSClosableWindowMask | $.NSMiniaturizableWindowMask | $.NSResizableWindowMask, $.NSBackingStoreBuffered, false ); // NSImageView this.firstpage = $.NSImageView.new; this.firstpage.setImageScaling($.NSScaleToFit); this.secondpage = $.NSImageView.new; this.secondpage.setImageScaling($.NSScaleToFit); this.window.contentView.addSubview(this.firstpage); this.window.contentView.addSubview(this.secondpage); // set this.archive = new ComipoliArchive(); this.archive.newArchive(PATH, false, false); this.nextPage(); // this.window.title = $("Title"); this.window.orderFrontRegardless; this.window.delegate = $.WinDelegate.new; this.window.makeKeyAndOrderFront(this.window); this.fitImage(); } nextPage() { let item = this.archive.getItem(this.num); this.firstpage.setImage(item); // item = this.archive.getItem(this.num + 1); this.secondpage.setImage(item); this.num += 2; } fitImage() { let aw = this.window.contentView.frame.size.width; let ah = this.window.contentView.frame.size.height; let iw = this.firstpage.image.size.width; let ih = this.secondpage.image.size.height; // left let f = this.secondpage.frame; f.size = $.NSMakeSize(aw/2, ah); this.secondpage.frame = f; // right f = this.firstpage.frame; f.size = $.NSMakeSize(aw/2, ah); f.origin = $.CGPointMake(aw/2, 0); this.firstpage.frame = f; } } function run(argv) { const app = $.NSApplication.sharedApplication; const window = new ComipoliWindow(app); app.setActivationPolicy($.NSApplicationActivationPolicyRegular); ObjC.registerSubclass({ name: "MenuAction", methods: { "nextPage:": { types: ["void", ["id"]], implementation: (sender)=> { window.nextPage(); } } } }); app.mainMenu = function() { function nm(title, action, key, target) { let item = $.NSMenuItem.new; if (target) item.target = target; item.title = $(title); item.action = action; item.keyEquivalent = $(key); return item; } // main const mainMenu = $.NSMenu.new; // Menubar const itemApp = $.NSMenuItem.new; const itemFile = $.NSMenuItem.new; mainMenu.addItem(itemApp); mainMenu.addItem(itemFile); // Drop Down Menu const menuApp = $.NSMenu.alloc.initWithTitle($("Comipoli")); const menuFile = $.NSMenu.alloc.initWithTitle($("File")); itemApp.submenu = menuApp; itemFile.submenu = menuFile; // Action menuApp.addItem(nm("Quit", "terminate:", "q", null)); // let ac = $.MenuAction.new; menuFile.addItem(nm("Next Page", "nextPage:", "p", ac)); // return mainMenu; }(); app.run; }
できた。
command+P でページめくりもできます。
意外に完成は早くなりそう。