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 でページめくりもできます。
意外に完成は早くなりそう。