Scriptの最近のブログ記事

2011年11月3日時点で最新のMojolicious 2.21では、AnyEvent::Twitter::StreamをuseしただけでWebSocket接続ができなくなります。

テストコード:

http://search.cpan.org/~sri/Mojolicious-2.21/lib/Mojolicious/Guides/Cookbook.pod#WebSocket [search.cpan.org]

のコードの頭に、use AnyEvent::Twitter::Stream をくっつけるだけで、Google Chromeではエラーになってしまいます。

Googlechromewebsocketerror

ソースを少し改造して、Firefox用のPrefixをつけてやると、Firefoxでも動くはずなのですが、use AnyEvent::Twitter::Stream をくっつけると、やっぱりエラーになります。

Mozillafirefoxwebsocketerror

テスト環境は、Basic 64-bit Amazon Linux AMI 2011.09 (AMI Id: ami-0a44f00b)をmicroインスタンスで起動して、

mkdir bin
cd bin
curl -LOk http://xrl.us/cpanm
chmod +x cpanm
cd
sudo passwd root
su
yum install perl-devel
yum install gcc
yum install make
yum install openssl-devel
cpanm EV
cpanm Time::HiRes
cpanm Mojolicious
cpanm AnyEvent::Twitter::Stream
exit

までした状態で、上記のテストコードを

perl test.pl daemon

で実行しました。

回避方法としては、Mojolicious 2.19まで落とす、というのが一応有効です。今まで動いていたコードでWebSocket接続できなくなった時は疑ってみるといいかもです。

var Hoge = function() {};
Hoge.prototype = {
  hoge: function() {
  },
  do: function() {
    alert('do!');
  }
};
var fuga = new Hoge;
fuga.do();

ていうコードをFirefoxで動かすと通るが、Windows版Safari 5.0.5で動かすと、「Syntax Error: Parse error」が出る。

doを、dodoとかに書き換えると、エラーはなくなる。

メソッド名じゃなくてプロパティ名としてならどうなのかーと思って、

var Hoge = function() {};
Hoge.prototype = {
  hoge: function() {
    alert(this.do);
  },
  do: 1
};
var fuga = new Hoge;
fuga.hoge();

とやると、やっぱりSafariだとだめ。

いっそのこと、

var Hoge = {};
Hoge.do = 1;

というのはどうだ、と思ったらこれすらだめ。

Safariだと、doって予約されているのかな?


(2011-11-02 23:31追記)

Mac版Safari 5.0.6と、Windows版Safari 5.1.1で試したら、問題がありませんでした。それを受けてタイトルを修正しました。

M.C.P.C.: AppleScriptでレコード文字列のデシリアライズのやり方がわからん

にて、AppleScriptのレコードを表現する文字列

{ creatorversion: "13.0", documentversion: "8.0" }

を、実際にAppleScriptのレコードとして解釈してもらうためにどうすればいいか、ていうことがわからなかったわけです。

ほかのスクリプト言語であれば、これをデシリアライズという表現をして、PerlやJavaScriptだったらevalっていうやつを使うとそれができるわけですが、AppleScriptとデシリアライズと検索しても出てこない。

んて、この前のエントリのコメント欄で教えていただきrun script "{...}"という書き方ができるというので、やってみました。

set str to " {creatorversion: \"13.0\", documentversion: \"8.0\"} " as string
set result to run script result
display alert ("Creator Version: " & (get creatorversion of result) & "
Document Version: " & (get documentversion of result))

Applescriptdeserialize01
▲スクリプトエディタに入力。エスケープ文字のバックスラッシュはOption+¥

Applescriptdeserialize02
▲実行

うまくいきました。これで、外部のソフトを使って処理した結果をAppleScriptで受け取って処理しやすくなりました。楽でいいですね!

AppleScriptを最近はじめたんですけれども、参考書が少なくて困っていたところ、こんな本があるのを発見。

これ自分で買って会社においておけばMac好きな人が勝手に読んでなんか作ってくれると思って4年ぐらい前に買って書棚においておいたんだけれども結局何もできなかったんで、Macを仕事で使う人≠Mac好きな人であることを実感。自分でやるしかないか。

それはよいとして、AppleScriptからPerlのスクリプトをdo shell scriptで呼んで、結果をAppleScriptに受け渡ししたいところなんですけれども、Perl側で

$VAR1 = {
          'creatorversion' => '13.0',
          'documentversion' => '8.0'
        };

みたいな連想配列になっているものを、AppleScriptでいうところのレコードの文字列

{ creatorversion: "13.0", documentversion: "8.0" }

にしてAppleScriptに渡して、これをレコードとして処理させたいわけです。

PerlからのシリアライズについてはJSONとかのライブラリ使うと作る必要ねーなーと思ったのですけれども(←あとで罠にはまるフラグ)、AppleScript側でデシリアライズする方法がわからん。

ググっても出てこんので、AppleScript界隈ではデシリアライズとか言わないんだろうなーとは思うんですけどね。

Adobe ExtendScript2、Adobe Bridge CS3、Adobe InDesign CS3などで動くJavaScriptで、

/*
if ( !ExternalObject.webaccesslib ) {
ExternalObject.webaccesslib = new ExternalObject('lib:webaccesslib');
}
*/
 
function HttpConn(url) {
    // コンストラクタ。ここでUser-Agent名や
    // timeout値など準備しておくといいよね
    this.url  = url;
    this.timeout = 5;
    this.redirect = 5;
    this.method = "GET";
    this.responseStatus = undefined;
    this.responseheaders = {};
    this.requestheaders = {};
    this._count = 0;
};
HttpConn.uri = function(uri) {
    var rex = new RegExp( 'http://([^:/]+)(?::(\d+))?(.+)' );
      // via http://pc11.2ch.net/test/read.cgi/php/1015692614/57
    var urlObj =[];
    if ( uri.match(rex) ) {
        urlObj.host = RegExp.$1;
        urlObj.port = RegExp.$2 ?RegExp.$2 :80;
        urlObj.path = RegExp.$3;
    }
    return urlObj;
};
HttpConn.prototype.execute = function () {
    var conn = new Socket;
    conn.timeout = this.timeout;
    var urlObj = HttpConn.uri(this.url);
    if (conn.open( urlObj.host + ':' + urlObj.port, 'binary' ) ) {
        var requestHeader ="";
        for (var i = 0; i < this.requestheaders.length / 2; i++) {
            requestHeader = this.requestheaders[i] +": "
            + this.requestheaders[i+1] + "\n";
        }
         conn.write ( this.method + " " + urlObj.path
        + " HTTP/1.0\n"
        + 'Host: ' + urlObj.host + "\n"
        + requestHeader
        + "\n");
        var reply = "";
        for ( ; ; ) {
            if (conn.eof) break;
            reply += conn.read(65536);
        }
        conn.close();
        var boundary =[];
        boundary[0]
        = reply.indexOf("\r\n"); //responseStatus/ResponseHeader
        boundary[1]
        = reply.indexOf("\r\n\r\n"); // responseHeader/body
        var response = {
            status: reply.substring(0, boundary[0]),
            header: reply.substring(boundary[0]+2, boundary[1]),
            body: reply.substring(boundary[1] + 4 ),
        }
        // response
        if (typeof(this.response) !="undefined"
            && this.response.constructor.name == "File") {
            this.response.open("w");
            this.response.write(response.body);
        }
        else {
            this.response = response.body;
        }
        var headerArray
        = response.header.substring(boundary[0] + 2).split("\n"); 
        this.responseheaders = [];
        var headerArrayPair = [];
        // responseheaders
        for (var i = 0; i < headerArray.length;i++) {
            if (headerArray[i].match(/(^.+?):(.+)/) ) {
                headerArrayPair.push( [RegExp.$1, RegExp.$2] );
            }
        }
        headerArrayPair.sort(function(a, b) {
            return (a[0] > b[0]) ? 1 : -1;
        });
        for (var i = 0; i < headerArrayPair.length; i++) {
                this.responseheaders.push( 
                    headerArrayPair[i][0], headerArrayPair[i][1] );
        }
        // responseStatus
        response.status.match(/(^HTTP\/\d\.\d) (\d+) (.+)/);
        this.responseStatus = RegExp.$2;
        if (this.responseStatus == "301"
        || this.responseStatus == "302") {
            var url = "";
            for (var i = 0;  i < this.responseheaders.length; i+=2) {
                if (this.responseheaders[i] == "Location") {
                    url = this.responseheaders[i+1];
                    break;
                }
            }
            var http = new HttpConn(url);
            http.requestheaders = [ "User-Agent",
                app.name + "/" + app.version + " "
                + "(" + $.os + ")",
            ];
            http.execute() ;
            this.response = http.response;
            this.responseStatus = http.responseStatus;
        }
        return;
    } else {
        this.responseStatus = -1;
    }
};
 
 
var http = new HttpConn("http://www.yahoo.co.jp/");
http.requestheaders = [    "User-Agent",
    app.name + "/" + app.version + " "
    + "(" + $.os + ")",
 ];
//http.response = new File("/c/temp/robin.shtml");
 
http.execute();
 
alert(http.responseStatus);
alert(http.response);
 
// http.response.close();

てな感じで、HttpConnectionクラスのただWebから引っ張ってくるところだけ互換で使えるようなクラスHttpConnクラスを作っているんだけれども、今途中で止まっているのは、binaryで引っ張ってきてStringとして入れたコンテンツを如何にしてエンコーディングを変換しようか、ていうところです。Perlだったら、decode("utf8", $reply)の一文で済みそうなところなんだけれども、JavaScriptってどうすればいいんやろう。

InDesign - JavaScript小技 - 名もないテクノ手 [d.hatena.ne.jp]

OSとバージョンを得る
var my_fileSys = $.os;

うちの環境だと "Macintosh OS 10.5.5"と返ります。JSTG p.198

やってみた。


▲$.osをコンソールで入力してEnter

なるほど、$はHelper Objectといって、ビルトインなオブジェクトなんですね。

MacIE5のWebばっかりやっていたもので、$はスクリプトの最初で

function $(id){ return document.getElementById(id); }

をおまじないのごとく唱えるものと思い込んでいました。

あと、この手のことはJavaScript Tool Guideを参照すればいいのですね。

[PDF] JAVASCRIPT TOOLS GUIDE : p.199 [www.adobe.com]

せうぞーさん、ありがとうございました!

Illustrator 10~CS3のJavaScriptで、選択しているテキストパスに、現在のドキュメント名を入力します。ただし、Illustrator CS1では全く役に立ちませんw

用途として、Illustratorで手動面付けしている事業所さんなんかで、トンボ外にドキュメント名を表示することで、出力したものを取り違えないようにするのに使えます。

機能の割に長いのは、Illustrator 10~CS3対応するため、Illustrator 10においてCS以降のアクセス方法が使えるようなappオブジェクトを拡張しているからです。無駄だよね……

Filename: input_filename.jsx (Illustrator 10の場合は拡張子jsの方がいいのかな)

/*
  input_filename.jsx : 選択したテキストにファイル名を入力します
*/
(function(){
  // 機能拡張:AI10の場合、appオブジェクトを拡張
  if (typeof app == "undefined") {
    // Illustrator10の場合
    app = {};
    app.documents = documents;
    if (app.documents.length != 0) { 
      // activeDocumentが存在していないとアクセスすらできない
      app.activeDocument = activeDocument;
    }
  }
  // main
  if (app.documents.length != 0) { 
    // ドキュメントを1つ以上開いている
    var objDoc  = app.activeDocument;
    var selObjs = objDoc.selection;
    switch ( selObjs.length ) {
      case 0: // 選択無し
        alert("テキストパスを1つだけ選択してください。");
        break;
      case 1: // 1つだけ選択
        if ( (selObjs[0].typename == "TextFrame") // AICS以降
          || (selObjs[0].typename == "TextArtItem") ) {
          // TextFrameだった
          selObjs[0].contents = objDoc.name;
        }
        else { // TextFrame以外だった
          alert("テキストパスを選択してください。");
        }
        break;
      default: // 2つ以上選択
        alert("複数選択しています。"
            + "テキストパスを1つだけ選択しなおしてください。");
    }
  }
  else { // ドキュメントを開いていない
    alert("Illustratorで書類を開いてから実行してください。");
  }
})();

CodeReposにIllustratorのディレクトリ掘っていいかなー
http://svn.coderepos.org/share/platform/illustrator
あたりかな。

InDesignやIllustratorのJavaScriptで、選んでいるアイテムがなんであるか調べたいっていうことがあります。ブラウザのJavaScriptでいえば、DOMをdocument.getElementsByTagNameで絞ったあと、アイテムを1点ずつclassNameを見て照合するみたいなイメージになるでしょうか。

そんで、選んだオブジェクトが何であるか、というのを調べるコード、いろんなのが流通しているみたい。

(function(){
  var objDoc  = app.activeDocument;
  var selObjs = objDoc.selection;
  
  alert(selObjs[0].constructor.name); // JavaScript実装
  
  alert(selObjs[0].typename);         // PICTRIX風
  
  alert(selObjs[0].toString() );      // 古籏一浩風
  
})();

さてこれのうちどれを使うのがいいのかな。

MacIE5でArray.pushが必要になったんで、作りました。以下のコードをJavaScriptのコードに入れておくと、Arrayオブジェクトにpushメソッドが追加されます。pushメソッドが既にある場合は、上書きしません。

      // Array.pushをMacIE5に実装
      if (typeof Array.prototype.push != 'function') {
        Array.prototype.push = function (f){
          this[this.length] = f;
        }
      }


使うときは普通に

var x = ['hoge', 'fuga'];
x.push('hage');
alert(x.length); // 3
var s = '';
for (var i = 0; i < x.length; i++ ) {
  s += i + ':' + x[i] + ',';
}
alert(s); // 0:hoge,1:fuga,2:hage,

となります。


作ってから検索してみたら、先人が同じコードを作ってた。よくあることだねー
IE5.5以前ではArray.push()が使えないらしいので - へぼいいいわけ [d.hatena.ne.jp]

あと、同様に昔作ったMacIE5でdecodeURIを実装もご覧ください。

PerlのCAM::PDFモジュールを使って、PDFのフォント一覧を出し、特にフォントが埋め込まれているかどうか調べます。

実行例:

$ ./listfont.pl 2.pdf
$VAR1 = [
          {
            'subtype' => 'CIDFontType0',
            'embedded' => 'yes',
            'page' => 1,
            'embeddedtype' => 'Type 1 (CID)',
            'basefont' => 'RAUSIV+GothicBBBPro-Medium',
            'fontfamily' => 'þÿA-OTF N-0´0·0Ã0¯BBB Pro Medium'
          },
          {
            'subtype' => 'CIDFontType2',
            'embedded' => 'yes',
            'page' => 1,
            'embeddedtype' => 'TrueType (CID)',
            'basefont' => 'BCSASB+SymbolMT',
            'fontfamily' => 'Symbol'
          },
          {
            'subtype' => 'TrueType',
            'embedded' => undef,
            'page' => 1,
            'embeddedtype' => undef,
            'basefont' => 'ArialMT',
            'fontfamily' => 'Arial'
          }
        ];
$

ソースは次の通り。

Filename: listfont.pl

#!/usr/bin/perl
use strict;
use warnings;
use CAM::PDF;
use Data::Dumper;
binmode STDOUT => ':utf8';
my $infile = shift;
my $doc = CAM::PDF->new($infile) || die "$CAM::PDF::errstr\n";
 
my $fonts;
for my $page (1 .. $doc->numPages()) {
  foreach my $fontname (sort $doc->getFontNames($page)) {
    my $font = $doc->getFont($page, $fontname);
    $font = parsefont($font);
    push @$fonts,
    {
      page         => $page,
      embedded     => $font->{embedded},
      fontfamily   => decode_fontfamily($font->{fontfamily}),
      basefont     => $doc->getValue($font->{BaseFont}),
      subtype      => $doc->getValue($font->{Subtype}),
      embeddedtype => $font->{embeddedtype},
    };
  }
}
 
print Dumper($fonts);
exit;
 
 
sub parsefont {
  my $font = shift;
  my $fontdescriptor  = $font->{FontDescriptor};
  if ($fontdescriptor) {
    my $ref = $doc->getValue($fontdescriptor);
    $font->{fontfamily} = $doc->getValue($ref->{FontFamily});
    my $fontfile  = $doc->getValue($ref->{FontFile });
    my $fontfile2 = $doc->getValue($ref->{FontFile2});
    my $fontfile3 = $doc->getValue($ref->{FontFile3});
    if ( ($fontfile) || ($fontfile2) || ($fontfile3) ) {
      $font->{embedded} = 'yes';
      $font->{embeddedtype} ="Type 1"         if $fontfile;
      $font->{embeddedtype} ="TrueType (CID)" if $fontfile2;
      $font->{embeddedtype} ="Type 1 (CID)"   if $fontfile3;
    }
    return $font;
  }
  my $descendantfonts = $doc->getValue($font->{DescendantFonts});
  if ($descendantfonts) {
    if (ref $font->{DescendantFonts}->{value}) {
      return parsefont(
               $doc->getObjValue(
                 $font->{DescendantFonts}->{value}->[0]->{value}
               )
             );
    } else {
      return parsefont(
               $doc->getObjValue(
                 $doc->getObjValue(
                   $font->{DescendantFonts}->{value}
                 )->[0]->{value}
               )
             );
    }
  }
}
 
sub decode_fontfamily {
  my $str = shift;
  $str =~s/#([0-9A-F][0-9A-F])/pack('C', hex($1))/eg;
  return $str;
}
 
__END__

再帰処理の部分で、戻り値を2通り持たせているのですけれども、これは、

1647 0 obj<</Subtype/Type0/DescendantFonts[1646 0 R]/BaseFont/DIEAIA+SymbolMT/ToUnicode 1644 0 R/Encoding/Identity-H/Type/Font>>

$VAR1 = {
          'DescendantFonts' => bless( {
                                        'gennum' => '0',
                                        'value' => [
                                                     bless( {
                                                              'gennum' => '0',
                                                              'value' => '1646',
                                                              'type' => 'reference',
                                                              'objnum' => '1647'
                                                            }, 'CAM::PDF::Node' )
                                                   ],

と取られ、

9 0 obj<</Subtype/Type0/DescendantFonts 15 0 R/BaseFont/RAUSIV+GothicBBBPro-Medium/Encoding/Identity-H/Type/Font>>

$VAR1 = {
          'DescendantFonts' => bless( {
                                        'gennum' => '0',
                                        'value' => '15',
                                        'type' => 'reference',
                                        'objnum' => '9'
                                      }, 'CAM::PDF::Node' ),

みたいに取られまして、

よくわかんないけれども元のPDFで2通りの記述があるのがそのままパースされちゃうので、分岐してみました。

こんな危なっかしいコード書いていることでもわかるとおり、これで決定版というわけではなく(もれなく間違いなく調べられるかどうかは僕も不明ということ)。普通にAcrobatやAdobe ReaderでCtrl+Dしてフォントタブを選択して調査したほうがいいかもしれません。

月別 アーカイブ

ウェブページ

OpenID対応しています OpenIDについて
Powered by Movable Type 5.2.7

このアーカイブについて

このページには、過去に書かれたブログ記事のうちScriptカテゴリに属しているものが含まれています。

前のカテゴリはRSSです。

次のカテゴリはTypePadです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。