Archive for 12月, 2013

2013/12/31 AppleScriptObjCでTicker表示

株価や新着ニュースを横スクロール表示して読ませる「Ticker」は、ひじょーーに昔から存在しているオーソドックスな表示方法ですが、いざ実装しようとすると案外大変です。

そんなTickerのObjective-Cのサンプルコードを探し、AppleScriptObjCのプロジェクト内に突っ込んでいろいろ試してみました。

ticker.png

いじくっている間に、Ticker自体の動作原理がひじょーによく分ったものの、今回使ったサンプルのままでは一回り表示するとそこで終わってしまうので、エンドレスに指定のテキストを横スクロール表示し続けるようにできるとなおよいでしょう。Objective-CのコードをすべてAppleScriptObjCに書き換えればいかようにでも変更できそうですが、何かアプローチが間違っているようないないような……。

Ticker自体の実装方法については、いろいろとやりかたがあるようなので、このサンプルにこだわらず、さらにいろいろ試してみたいところです。とくに、Quartz Composerで実装すると(考えることが少なくて)楽そうな気がしています。

Ticker自体の使い道としては、ファイル名表示を行うさいに、長いファイル名の場合にfoldingして(一部省略して)表示するよりも、横スクロールさせたほうが有用性が高いのでその方面に使う、といったところでしょうか。

とりあえず、ひろってきたTickerサンプルはARC有効の環境ではコンパイルが通らなかったので、Objective-Cのソース自体に手を加えています。ビルドが通って動いているので大丈夫だと思いますが、問題があればご指摘ください。

→ Xcodeプロジェクト(75KB)

AppleScriptObjCファイル名:AppDelegate.applescript

– AppDelegate.applescript
– tickerTest

– Created by Takaaki Naganoya on 2013/12/31.
– Copyright (c) 2013年 Takaaki Naganoya. All rights reserved.


script AppDelegate
  
  property parent : class “NSObject”
  
property TickerView : class “TickerView”
  
  on applicationWillFinishLaunching:aNotification
    
  end applicationWillFinishLaunching:
  
  
  on awakeFromNib()
    
    TickerView’s setStringValue:“   日本語でティッカー表示するために元のソースのフォント名を書き換えています〜。あと、ARC対応のために若干書き換え。”
    
TickerView’s startAnimation:me
    
  end awakeFromNib
  
  
  on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/30 USB Missile Launcher NZでUSBミサイルランチャーを操作?

USBミサイルランチャーという、USB経由でコントロールできる(スポンジ製の)ミサイルを発射できるおもちゃ系PC周辺機器があり、一時期さまざまな場所で話題になっていました。

mis3.jpg

この周辺機器をMacからコントロール可能にするのが、「USB Missile Launcher NZ」と呼ばれるソフトウェアです。

作者はニュージーランド在住のDavid G. Wilson氏。「NZ」というのはニュージーランドを意味しているとかいないとか。

このソフトウェアがAppleScriptに対応しており、AppleScriptから制御できるようです。

mis2.png
▲USB Missile Launcher NZ v1.8.1の起動画面

mis1.png
▲USB Missile Launcher NZのAppleScript用語辞書

「ようです」というのは、肝心のUSBミサイルランチャー自体の流通が終わってしまったらしく、たまにオンラインストアなど(Amazonとか)で買えないか探してみてはいるものの、見当たらなくなってしまったためです。

……USBミサイルランチャーでさんざん遊んで飽きてしまった方は、ちょっとだけ貸していただけると幸いです。

監視カメラの制御アプリとUSB Missile Launcher NZを組み合わせると、カメラ映像の動体検出を行って、侵入者があったら(スポンジ製の)ミサイルを発射するといった「ほほえましい」監視+迎撃システムが開発できます。

2013/12/28 文字列比較でNFKC Casefoldの影響を除外するには?

AppleScriptの文字列比較においては、カタカナとひらがな、半角と全角文字が同一視されており、「これってどーなの?」と指摘しつつも、それに対してApple側は「Unicodeの規格にのっとった実装だ」と一貫して主張してきました。

この状況を指して、Scripter側は「Appleがバカだからこういう実装になっている」と言っていますし、Apple側は「規格に則っているので問題ない」と、主張が平行線をたどっています。この規約を指してどう呼ぶのか謎でしたが、「NFKC Casefold」というルールによるものだと判明。

確かに、同一視できる機能は便利ですが、「同一視しない」比較処理が一切行えないというのはもっと困ります。

# ただ、言うほどトラブル源になったこともないのですが、、、、

症状の実例と、対策案を2つほど提示してみます。どれをどのように用いるかは、読んだ人次第です。

症状の実例

スクリプト名:NFKC Casefold_ひらがなカタカナ同一視
set a to “アップルスクリプト” –全角カタカナ
set b to “あっぷるすくりぷと” –全角ひらがな

if a = b then
  display dialog “Same String”
end if

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:NFKC Casefold_半角全角同一視
set a to “アップルスクリプト” –全角カタカナ
set b to “アップルスクリプト” –半角カタカナ

if a = b then
  display dialog “Same String”
end if

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:NFKC Casefold_半角全角同一視(数字)
set a to “10000” –全角数字
set b to “10000″ –半角数字

if a = b then
  display dialog “Same String”
end if

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:NFKC Casefold_記号とカタカナ同一視
set a to “1”
set b to “1リットル”

if a = b then
  display dialog “Same String”
end if

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

対策1〜文字コードダンプしてみる

そのまま文字変数に入れると、厳密な比較が行えません。そこで、文字コードをそのままhexdumpしてみます。

hexdumpした結果同士で比較すれば、厳密に文字コードの違いを比べられます。

生のままの文字列とhexdumpしたコードを別々に保持しておいて、厳密な比較のためにはhexdumpしたコードを用いるといった処理もできますね(かったるいけど)。

do shell script "echo アップルスクリプト | /usr/bin/hexdump"
0000000 e3 82 a2 e3 83 83 e3 83 97 e3 83 ab e3 82 b9 e3
0000010 82 af e3 83 aa e3 83 97 e3 83 88 0a
000001c
do shell script "echo アップルスクリプト | /usr/bin/hexdump"
0000000 ef bd b1 ef bd af ef be 8c ef be 9f ef be 99 ef
0000010 bd bd ef bd b8 ef be 98 ef be 8c ef be 9f ef be
0000020 84 0a
0000022
スクリプト名:NFKC Casefold_対策1(hexdump)
set a to “アップルスクリプト” –全角カタカナ
set b to “アップルスクリプト” –半角カタカナ

set aStr to do shell script “echo “ & a & ” | /usr/bin/hexdump”
set bStr to do shell script “echo “ & b & ” | /usr/bin/hexdump”

if aStr = bStr then
  display dialog “Same String”
end if

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

対策2〜ASOCで処理してみる

通常のAppleScriptの処理系ではなく、AppleScriptObjCのプログラム内でNSStringを用いて文字列データを保持し、比較してみます。

ASOCで処理してみると、通常のAppleScriptでは検出できないケースでも大丈夫でした。

AppleScriptObjCファイル名:AppDelegate.applescript

– AppDelegate.applescript
– stringTest

– Created by Takaaki Naganoya on 2013/12/27.
– Copyright (c) 2013年 Takaaki Naganoya. All rights reserved.


script AppDelegate
  
  property parent : class “NSObject”
  
  
  on applicationWillFinishLaunching:aNotification
    
    set aList to {{“1”, “1リットル”}, {“アップル”, “あっぷる”}, {“アップルスクリプト”, “アップルスクリプト”}, {“10000”, “10000″}}
    
    repeat with i in aList
      set {a1, b1} to i
      
compareStringsA_B_(a1, b1)
    end repeat
    
  end applicationWillFinishLaunching:
  
  
  on applicationShouldTerminate:sender
    – Insert code here to do any housekeeping before your application quits
    
return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
  –文字列比較をASOC(Cocoa)とAppleScriptで実施するテストルーチン
  
on compareStringsA:a b:b
    
    set aStr to current application’s NSString’s stringWithString:a
    
set bStr to current application’s NSString’s stringWithString:b
    
    tell current application
      if aStr = bStr then
        display dialog “ASOC:Equal”
      else
        display dialog “ASOC:Not equal”
      end if
    end tell
    
    tell current application
      if a = b then
        display dialog “AS:Equal”
      else
        display dialog “AS:Not Equal”
      end if
    end tell
    
    
  end compareStringsA:b:
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/19 Calendar.appのカレンダーを名称で抽出。Reminderのlistが見えてしまうものは除く

カレンダー(Calendar.app。旧称=iCal)上でカレンダーを名称で指定して取得するAppleScriptです。

カレンダー上では、自分自身のカレンダーだけでなく、あろうことかリマインダーのリストまで取得できてしまいます。これは、OS Xのバグというよりは、バカというべきか……カレンダー(Calendar.app)上では両者の識別方法はまったくありません。

さらに、両方とも「ホーム」といった同じ名前のカレンダー/リストがデフォルトで存在しており、カレンダー(Calendar.app)上では両者がまったく識別できません。

これは、Wordでオープン中の書類を問い合わせたら、ついでにPowerPointの書類まで返ってきて、さらにAppleScriptからはWord書類もPowerPoint書類も区別がつかない状態、といったら分りやすいでしょうか。関係ない他のアプリケーションのデータが見えてしまうなんて、バカもやすみやすみ言っていただきたいものです。

cal_rem.png

海外の仕事で調査中にこれに気付いて真っ青になりました。リマインダー側のリストの名称を手作業で替えてもらってなんとかなりましたが、1年に数回ある「クパティーノの某社に殺意を感じる一瞬」のうちの1回をカウントアップしてしまった次第。

AppleScriptから操作する分には、AppleScriptで取得できるデータの範囲内でしか処理できません。この、Appleのバグ的な仕様のためにひどい目にあわされています。バグレポートに書いたものの、Appleのエンジニアは文字が読めないか、自分たちで仕事をしていないためか、今日に至るも直っていません。

そのため、これを避けるための処理を書いてみました。動作原理は(苦労させられている割には)わりと簡単です。

まず、リマインダーにリストのIDをすべて問い合わせておいて、カレンダー(Calendar.app)で取得されるカレンダーのIDと付け合わせを行って、「カレンダー(Calendar.app)上で見えてしまう、リマインダーのリスト」を除外するという処理を行っています。

スクリプト名:Calendar.appのカレンダーを名称で抽出。Reminderのlistが見えてしまうものは除く
set aRes to getCalendarObjFromCal(“ホーム”) of me
–> {calendar id “8D0AC013-2AF0-4EA1-A563-AE59C811EB36″ of application “Calendar”}

–Calendar.appのカレンダーを名称で抽出。Reminderのlistが見えてしまうものは除く
on getCalendarObjFromCal(aName)
  
  
–先に、リマインダーのカレンダー(list)のIDを取得しておく
  
tell application “Reminders”
    set rList to id of every list
  end tell
  
  
–カレンダーを抽出するさいに、名称でしぼりこむほか、リマインダーのカレンダーを除外する
  
tell application “Calendar”
    set r2List to every calendar whose name is equal to aName
    
    
set r3List to {}
    
    
repeat with i in r2List
      set anID to uid of i
      
if anID is not in rList then
        set the end of r3List to (contents of i)
      end if
    end repeat
    
    
return r3List
  end tell
  
end getCalendarObjFromCal

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/18 Notesの指定のメモからタグを除去

OS X 10.8あたりからOS標準装備アプリに加わった「メモ」(Notes.app)の、指定のメモ(note)から内容を取得してタグを外すAppleScriptです。

→ macOS 10.10以降はこちら「Notesの指定エントリの本文をプレーンテキストとして取得」

メモ(以下、Notes.app)は文字色や文字種類など、さまざまな指定が可能なマルチスタイルのテキストを保存可能なメモアプリケーションです。

勝手にiCould経由でiOSデバイスとシンクロしてくれるので、iPhoneのメモに何か命令を書いておくと、それをMac側で検知して結果をNotes.app経由でiPhoneに戻すような処理もAppleScriptで記述可能です(そんな面倒なことをするよりも、メッセージ(旧称:iChat)経由でダイレクトにやり取りしたほうが早いですが)。

つまり、Notesやリマインダーは、iCloudを経由してiOSデバイスからMacをコントロールするための「経路」としても使えるものです(そんなかったるいことをするよりも、メッセージで直接コンタクトしたほうがいいですが)。

そんな遠大な野望を実現するための第一歩として(?!)、Notes.appの特定のメモから、本文部分を取得する処理を書いてみました。

Notes.appは本文を取得してみると「タグがついたままのHTMLっぽい何かのテキスト」で返してくるため、プレーンなテキストに変換したい気持ちでいっぱいです。

……そんなに難しい処理でもないので、さくっと書いてしまいました。

書いておいてナンですが、あんまり実用性はないですね。

notes1.png

notes2.png

いろいろ変換を行ってみたものの、アプリケーション上とそっくり同じ位置で改行されるとか、そういう風にはならず……あちらを立てればこちらが立たず……そんなに気合いを入れるようなものでもないので、こんなもんでしょう。

スクリプト名:Notesの指定のメモからタグを除去
tell application “Notes”
  tell note “伊集院光 「ファミ通と僕」出版記念サイン会”
    set aText to body
  end tell
end tell

set a1Text to repChar(aText, “<br>”, return) of me
set a1Text to repChar(a1Text, “<div>”, return) of me
set a1Text to repChar(a1Text, “ ”, return) of me
set a1Text to detagText(a1Text) of me

a1Text
–>
(*
伊集院光 「ファミ通と僕」出版記念サイン会
新宿紀伊国屋書店本店 2巻同時予約した場合にかぎり、先着300名に整理券を配布
整理券の配布7/31 10:00本店2Fなくなり次第終了

整理券と引き換えで本2冊サイン本
サイン本には本人の名前を入れる
*)

–てきとーにタグを外す
on detagText(allText)
  repeat
    if (allText contains “< ") and (allText contains “>”) then
      set sOffst to offset of “< " in allText
      
set eOffst to offset of “>” in allText
      
      
set aTag to text sOffst thru eOffst of allText
      
      
set allText to repChar(allText, aTag, “”) of me
    else
      return allText
    end if
  end repeat
end detagText

–文字置換ルーチン
on repChar(origText, targStr, repStr)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

–offset命令の実行を横取りする
on offset of searchStr in str
  set aRes to getOffset(str, searchStr) of me
  
return aRes
end offset

on getOffset(str, searchStr)
  set d to divideBy(str, searchStr)
  
if (count d) is less than 2 then return 0
  
return (length of item 1 of d) + 1
end getOffset

on divideBy(str, separator)
  set delSave to AppleScript’s text item delimiters
  
set the AppleScript’s text item delimiters to separator
  
set strItems to every text item of str
  
set the AppleScript’s text item delimiters to delSave
  
return strItems
end divideBy

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/16 OS X 10.9で指定PDFのページ数をかぞえる(AppleScriptObjC版)

OS X 10.9, Mavericks上で指定のPDFのページ数をかぞえるAppleScriptのAppleScriptObjC版(要Xcode)です。

PDFのページ数をかぞえる処理は、ひじょーに重要で応用範囲が広いものなので、先日あわてて即興で1本書きました(迅速に対応できないとまずい案件があったので)。mdlsコマンドで間接的にPDFのページ数を取得するというものでしたが、常識的な範囲内ではとくに問題なく動きました。

ただ……あれやこれやと「常識的な範囲内」についての条件説明が必要なため、まっとうな方法(AppleScriptObjCでCocoaを叩く)=「直接機能にアクセスして情報を取得」する方法で作っておく必要がありました。

そこで、仕事の合間を見て試作品を作成。ウィンドウのボタンをクリックすると、PDF選択ダイアログが表示されるため、PDFを選択するとページ数をダイアログで返します。

pdfc1.png

pdfc2.png

AppleScriptObjCファイル名:AppDelegate.applescript

– AppDelegate.applescript
– countPDFtest

– Created by Takaaki Naganoya on 2013/12/12.
– Copyright (c) 2013年 Takaaki Naganoya. All rights reserved.


script AppDelegate
  property parent : class “NSObject”
  
  property nsurl : class “NSURL”
  
property PDFDocument : class “PDFDocument”
  
  
  on applicationWillFinishLaunching:aNotification
    
  end applicationWillFinishLaunching:
  
  
  on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
  on clicked:sender
    
    tell current application
      set aFile to choose file of type {“com.adobe.pdf”}
    end tell
    
    set aRes to pdfPageCount_(aFile as string)
    
    tell current application
      display dialog aRes as string
    end tell
    
  end clicked:
  
  
  
  –指定PDFのページ数をかぞえる(10.9対応。普通にPDFpageから取得)
  
–パラメータ:PDFファイルのHFS path(string)
  
–返り値:PDFファイルのページ数(整数値)
  
on pdfPageCount:aFile
    
    tell application “Finder”
      set aFileURL to (URL of file aFile)
    end tell
    
    set aFileURL to aFileURL as Unicode text
    
set theURL to nsurl’s URLWithString:aFileURL
    
    set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL
    
set aRes to aPDFdoc’s pageCount()
    
    return aRes as integer
    
  end pdfPageCount:
  
  
  
  –指定PDFのページ数をかぞえる(10.9対応。普通にPDFpageから取得)
  
–パラメータ:PDFファイルのPOSIX path(quoteされていない)
  
–返り値:PDFファイルのページ数(整数値)
  
on pdfCountPOSIXpath:aPDFPosixPath
    
    set aFile to (aPDFPosixPath as POSIX file) –POSIX pathからPOSIX fileにcast
    
    tell application “Finder”
      set aFileURL to (URL of (aFile as alias)) –aliasからでないとURLが取得できない
    end tell
    
    set aFileURL to aFileURL as Unicode text
    
set theURL to nsurl’s URLWithString:aFileURL
    
    set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL
    
set aRes to aPDFdoc’s pageCount()
    
    return aRes as integer
    
  end pdfCountPOSIXpath:
  
end script

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/12 OS X 10.9で指定PDFのページ数をかぞえる

OS X 10.9, Mavericks上で指定のPDFのページ数をかぞえるAppleScriptです。

以前、指定のPDFのページ数をかぞえるAppleScriptを掲載していましたが、これがOS X 10.9, Mavericks上で動かないということが(いまごろ)判明。かなり利用範囲が広く重要なルーチンであるため、あわてて対策を(ひとりで)検討してみました。

まっとうで難しそうな方法(AppleScriptObjCでCocoaを叩く)と、簡単で手抜きな方法(GUI Scriptingではなく)を思いつきましたが、ここでは後者を紹介します。

ちょっと表現が刺激的になってしまいましたが、前者は「直接機能にアクセスして情報を取得」、後者は「間接的に機能にアクセスして情報を取得」しているものとお考えください。

まず、おもむろにFinderでPDFのファイルの情報を、「情報を見る」コマンドを使って表示させてみてください。

pdf0.png

そこには、なにげにPDFのページ数が……。

pdf1.png

だったら、これを取得するのが一番簡単です。この情報は、mdlsコマンドで取得できるため、パスワードなし/パスワード付きのPDFのページ数をいろいろ調査。

MBPretina:~ xxxx$ mdls /Users/xxxx/Desktop/blazingshadow1_trial.pdf
_kTimeMachineIsCreationMarker  = 1
_kTimeMachineNewestSnapshot    = 4001-01-01 00:00:00 +0000
_kTimeMachineOldestSnapshot    = 2013-11-29 12:56:53 +0000
kMDItemContentCreationDate     = 2013-11-29 14:24:39 +0000
kMDItemContentModificationDate = 2013-11-29 14:24:39 +0000
kMDItemContentType             = "com.adobe.pdf"
kMDItemContentTypeTree         = (
    "com.adobe.pdf",
    "public.data",
    "public.item",
    "public.composite-content",
    "public.content"
)
kMDItemCreator                 = "Adobe InDesign CC (Macintosh)"
kMDItemDateAdded               = 2013-11-29 14:24:50 +0000
kMDItemDisplayName             = "blazingshadow1_trial.pdf"
kMDItemEncodingApplications    = (
    "Adobe PDF Library 10.0.1"
)
kMDItemFSContentChangeDate     = 2013-11-29 14:24:39 +0000
kMDItemFSCreationDate          = 2013-11-29 14:24:39 +0000
kMDItemFSCreatorCode           = ""
kMDItemFSFinderFlags           = 0
kMDItemFSHasCustomIcon         = (null)
kMDItemFSInvisible             = 0
kMDItemFSIsExtensionHidden     = 0
kMDItemFSIsStationery          = (null)
kMDItemFSLabel                 = 0
kMDItemFSName                  = "blazingshadow1_trial.pdf"
kMDItemFSNodeCount             = (null)
kMDItemFSOwnerGroupID          = 20
kMDItemFSOwnerUserID           = 504
kMDItemFSSize                  = 5174587
kMDItemFSTypeCode              = ""
kMDItemKind                    = "PDF"
kMDItemLastUsedDate            = 2013-12-03 02:55:41 +0000
kMDItemLogicalSize             = 5174587
kMDItemNumberOfPages           = 26
kMDItemPageHeight              = 510.236
kMDItemPageWidth               = 360
kMDItemPhysicalSize            = 5177344
kMDItemSecurityMethod          = "Password Encrypted"
kMDItemTitle                   = "機動戦士ガンダム ブレイジングシャドウ (1) 試読版"
kMDItemUseCount                = 5
kMDItemUsedDates               = (
    "2013-11-30 15:00:00 +0000",
    "2013-12-02 15:00:00 +0000"
)
kMDItemVersion                 = "1.3"

とくに問題はないようです。

……というわけで、mdlsコマンドで調査して値を返すことにしてみました。

少々書き直して、エラー処理を改善したのとエラー時に0ではなく1を返すことにしました。些細な変更ですが、mdlsコマンドが実行できるためにはmdworkerがバックグラウンドで書類のspotlight用メタ情報を作成しておく必要があり、随時(OS側の都合で)処理されているためタイミングによっては「メタ情報が存在しない瞬間」が存在するかもしれない、という杞憂に近い懸念があったので、0ではなく1と返しておくことにしておきました(1ページも存在しないPDFはないだろう、ということで)。

万全を期するのであれば、指定のPDFのSpotlightメタ情報生成をmdworkerに強制的に行わせるぐらいはしてもいいのかもしれませんが……やや心配のしすぎのような気もします。

むしろ、システム環境設定の「Spotlight」の「プライバシー」で指定されているフォルダ以下に存在するPDFでは使えない、という注意点を書いておいたほうがよいでしょうか。あとは、OSのインストール直後やアップデート直後でSpotlightのインデックス作成が終了していない時点での実行は避けるべきだと考えます。

spot3.png

MBPretina:~ xxxx$ mdls /Users/xxxx/Documents/Roxio Converted Items/blazingshadow1_trial.pdf
kMDItemFSContentChangeDate = 2013-11-29 14:24:39 +0000
kMDItemFSCreationDate      = 2013-11-29 14:24:39 +0000
kMDItemFSCreatorCode       = ""
kMDItemFSFinderFlags       = 0
kMDItemFSHasCustomIcon     = 0
kMDItemFSInvisible         = 0
kMDItemFSIsExtensionHidden = 0
kMDItemFSIsStationery      = 0
kMDItemFSLabel             = 0
kMDItemFSName              = "blazingshadow1_trial.pdf"
kMDItemFSNodeCount         = 5174587
kMDItemFSOwnerGroupID      = 20
kMDItemFSOwnerUserID       = 504
kMDItemFSSize              = 5174587
kMDItemFSTypeCode          = ""

▲同じPDFをシステム環境設定の「Spotlight」>「プライバシー」で検索対象外の指定フォルダに移動させると、mdlsコマンドでPDF内の詳細情報が取得できない

スクリプト名:OS X 10.9で指定PDFのページ数をかぞえる
set aPDF to choose file of type {“com.adobe.pdf”}
set aPOSIX to POSIX path of aPDF
set aRes to pdfCount(aPOSIX) of me

on pdfCount(aPDFPosixPath)
  
  
–mdlsコマンドでPDFのページ数の属性情報を取得
  
set aRes to do shell script “/usr/bin/mdls -name kMDItemNumberOfPages “ & quoted form of aPDFPosixPath
  
  
–結果からページ数の部分のみを抽出して返す
  
try
    set aInd to offset of “=” in aRes
    
set bText to text (aInd + 2) thru -1 of aRes
    
set aNum to bText as integer
    
return aNum
  on error
    return 1 –エラー時
  end try
  
end pdfCount

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/09 Maps.appで開始地点と到着地点の住所を指定して経路表示 v2

OS X 10.9, Mavericksから標準装備になったApple純正地図アプリケーション「マップ」(Maps.app)で、指定の2つの住所(名称)の間の経路を、通常地図形式で表示するAppleScriptです。

map31.png

map4.png

住所で番地まで指定するよりも、場所の名前(○○駅 など)を指定したほうが、検索エリアの見た目はいい感じになりますね。

スクリプト名:Maps.appで開始地点と到着地点の住所を指定して経路表示 v2

set fromAdr to "中村橋駅"
set toAdr to "よみうりホール"

set fromAdr to encodeURL(fromAdr) of me
set toAdr to encodeURL(toAdr) of me

set aURL to "http://maps.apple.com/?" & "saddr=" & fromAdr & "&" & "daddr=" & toAdr

open location aURL

on encodeURL(str)
  return do shell script ("python -c \"import sys, urllib; print urllib.quote(sys.argv[1]) \" " as Unicode text) & quoted form of str
end encodeURL

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/09 Maps.appで開始地点と到着地点の住所を指定して経路表示

OS X 10.9, Mavericksから標準装備になったApple純正地図アプリケーション「マップ」(Maps.app)で、指定の2つの住所の間の経路を、通常地図形式で表示するAppleScriptです。

map12.png

map21.png

実行すると、かならず「自動車」による経路情報を表示します。徒歩による経路情報を表示するためには、ユーザーの操作(かGUI Scriptingによるコントロール)を必要とします。

右側の検索欄に表示される住所は西武池袋線の中村橋駅を指定したはずなのですが、別の(同ブロック内にある)店の名前になってしまっています。たしか、改札の中にあるコンビニのはずなので……車で乗り付けられない上に、改札の中に入らないと店名を確認できません。

URLプロトコルの資料を見ると、始点と終点に指定できるのは住所のみ。緯度、経度は指定できないようです(指定できてもおかしくないですが、、、)。

スクリプト名:Maps.appで開始地点と到着地点の住所を指定して経路表示

set fromAdr to “東京都練馬区中村北4-2-1″
set toAdr to “東京都千代田区有楽町1-11-1″

set fromAdr to encodeURL(fromAdr) of me
set toAdr to encodeURL(toAdr) of me

set aURL to “http://maps.apple.com/?” & “saddr=” & fromAdr & “&” & “daddr=” & toAdr
open location aURL

on encodeURL(str)
  return do shell script (“python -c \”import sys, urllib; print urllib.quote(sys.argv[1]) \” “ as Unicode text) & quoted form of str
end encodeURL

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/08 iPhotoで選択した写真から緯度経度情報を取得してMaps.appで緯度経度とズームレベルを指定して地図表示

iPhoto 9.5上で選択した写真から緯度経度情報を取得して、その情報をマップ(Maps.app)で位置表示、同時にズームレベルを指定するAppleScriptです。

GPSつきのカメラ、たとえばiPhoneで撮影した写真であれば(GPSの使用を許可していれば)自動で緯度経度情報が写真に添付され、写真がiCloud経由でMacのiPhotoにも自動配信されます。この、緯度経度情報つきの写真をiPhoto上で選択している状態を実行の前提条件として想定しています。

iphoto1.png
▲行きつけの練馬区春日町のバッティングセンター

その緯度経度の情報のついた写真をOS X 10.9上のiPhoto 9.5上で選択しておき、本AppleScriptを実行すると、マップ(Maps.app)上で場所を表示します。

住所情報を「東京都練馬区春日町」などと指定した場合には、最もズーム倍率(ズームレベル)が高い状態で地図が表示されてしまい、ズーム倍率の指定が無視されます。

そのために、緯度経度の情報を直接数値で指定して実験してみました。結果は予想どおり、緯度経度を数値で指定した場合にはズーム倍率の指定(z=)が有効になりました。

ズーム倍率(ズームレベル)は0から20ぐらいが有効で、うち0から2は指定した場所が画面の中心に来ないわ、世界地図全体が表示されるわ、関係ない場所が画面に出てくるわでほとんど意味がありません。

20以上は、たしかにズーム倍率が変わったようには見えるもののほとんど誤差程度にしか変化はなく、20が最大値だと判断してよさそうです。

zoom03.png
▲Zoom Level=3

zoom04.png
▲Zoom Level=4

zoom05.png
▲Zoom Level=5

zoom06.png
▲Zoom Level=6

zoom07.png
▲Zoom Level=7

zoom08.png
▲Zoom Level=8

zoom09.png
▲Zoom Level=9

zoom10.png
▲Zoom Level=10

zoom11.png
▲Zoom Level=11

zoom12.png
▲Zoom Level=12

zoom13.png
▲Zoom Level=13

zoom14.png
▲Zoom Level=14

zoom15.png
▲Zoom Level=15

zoom16.png
▲Zoom Level=16

zoom17.png
▲Zoom Level=17

zoom18.png
▲Zoom Level=18

zoom19.png
▲Zoom Level=19

zoom20.png
▲Zoom Level=20

zoom21.png
▲Zoom Level=21

zoom22.png
▲Zoom Level=22

zoom23.png
▲Zoom Level=23

zoom24.png
▲Zoom Level=24

スクリプト名:iPhotoで選択した写真から緯度経度情報を取得してMaps.appで緯度経度とズームレベルを指定して地図表示

set {aLat, aLon} to getLatitudeAndLongitudeFromAPhoto() of me
if {aLat, aLon} = {} then return

set aURL to “http://maps.apple.com/?ll=”
set zoomNum to 20
set bURL to (aLat as string) & “,” & (aLon as string)
set cURL to aURL & bURL & “&z=” & (zoomNum as string)

open location cURL

–iPhoto上で選択した写真から緯度経度情報を取得する
on getLatitudeAndLongitudeFromAPhoto()
  
  
tell application “iPhoto”
    set aSel to selection
    
if length of aSel = 0 then return {} –選択中の写真がない場合にはヌルのリストを返す
    
    
set aaSel to first item of aSel
    
set aProp to properties of aaSel
    
set aLatLocal to latitude of aProp –緯度
    
set aLonLocal to longitude of aProp —経度
  end tell
  
  
return {aLatLocal, aLonLocal}
  
end getLatitudeAndLongitudeFromAPhoto

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/07 Maps.appで住所検索+地図タイプを指定して表示

OS X 10.9, Mavericksから標準装備になったApple純正地図アプリケーション「マップ」(Maps.app)で指定の住所を、指定の地図形式で表示するAppleScriptです。

map0.png

地図形式は、「標準(m: map)」「地図+写真(h: hybrid)」『航空写真(k: satellite)」のうちから1つを指定可能です。

map11.png

スクリプト名:Maps.appで住所検索表示(タイプ=標準地図を指定)
set aURL to “http://maps.apple.com/?q=”
set bURL to “東京都港区六本木6丁目10番1号” –Apple Japanの住所
set cURL to aURL & encodeURL(bURL) of me & “&t=m” –「標準」の地図を指定

open location cURL

on encodeURL(str)
  return do shell script (“python -c \”import sys, urllib; print urllib.quote(sys.argv[1]) \” “ as Unicode text) & quoted form of str
end encodeURL

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

map2.png

スクリプト名:Maps.appで住所検索表示(タイプ=地図+写真を指定)
set aURL to “http://maps.apple.com/?q=”
set bURL to “東京都港区六本木6丁目10番1号” –Apple Japanの住所
set cURL to aURL & encodeURL(bURL) of me & “&t=h” –「地図+写真」の地図を指定

open location cURL

on encodeURL(str)
  return do shell script (“python -c \”import sys, urllib; print urllib.quote(sys.argv[1]) \” “ as Unicode text) & quoted form of str
end encodeURL

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

map3.png

スクリプト名:Maps.appで住所検索表示(タイプ=航空写真を指定)
set aURL to “http://maps.apple.com/?q=”
set bURL to “東京都港区六本木6丁目10番1号” –Apple Japanの住所
set cURL to aURL & encodeURL(bURL) of me & “&t=k” –航空写真を指定

open location cURL

on encodeURL(str)
  return do shell script (“python -c \”import sys, urllib; print urllib.quote(sys.argv[1]) \” “ as Unicode text) & quoted form of str
end encodeURL

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/07 Maps.appで住所検索表示

OS X 10.9, Mavericksから標準装備になったApple純正地図アプリケーション「マップ」(Maps.app)で指定の住所を表示するAppleScriptです。

マップで住所なり緯度経度を指定して表示する方法は必ずあり、それはURL経由で行うということは分っていたのですが、まさか「http://」のプロトコル経由でできるとは思いませんでした。

そんなわけで、ものすごく安直にできてしまいます。プロトコルは「http://」ですが、Safari(などのWebブラウザ)が起動していない場合でも、起動を促されるのはMaps.appであり、本Scriptを実行するたびにSafariが立ち上がったりすることはありません。

AppleのSafari Developer Libraryサイト上にある「Apple URL Scheme Reference」の中に「Map Links」の項目があり、緯度経度の直接指定やら、表示地図種類の指定、ズームレベルの指定、2点間の住所指定などさまざまな表現が行える、とあります。

カスタムURLプロトコルを書く必要があると思っていたので、本当に意外でした。

map1.png

スクリプト名:Maps.appで住所検索表示
set aURL to “http://maps.apple.com/?q=”
set bURL to “東京都港区六本木6丁目10番1号” –Apple Japanの住所
set cURL to aURL & encodeURL(bURL) of me

open location cURL

on encodeURL(str)
  return do shell script (“python -c \”import sys, urllib; print urllib.quote(sys.argv[1]) \” “ as Unicode text) & quoted form of str
end encodeURL

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2013/12/01 プレビュー.appをAppleScriptから操作

10.9から正式にAppleScript対応になったOS X標準搭載のPDFブラウズアプリケーション「プレビュー.app」(Preview.app)をAppleScriptからコントロールするサンプルです。

preview_dic.png

たしか、プレビュー.appは(Mac OS Xの前身である)NeXTstepの頃から存在していたように思うのですが……それが2013年になってAppleScriptに対応したというのは感慨深いです。以前、AppleScript非対応の時代のプレビュー.appを強引にAppleScript対応にするという試みは行ったことがありますが、そこまで強引なことをしなくてすむのは、いいと思います。

……で、プレビュー.appをAppleScriptからコントロールできるとどういう用途に使えるのか? という話になります。目下、AppleScriptへの対応度の高いPDFビューワーといえばフリーソフトウェアの「Skim」が著名であり、フリーでなくとも「PDFpen」などもあるため、何をいまさら……という感はあります。

さらに、「AppleScriptに対応した」とはいうものの、AppleScript用語辞書を見ると、本当に「最低限」の制御ができるだけの内容です。

アプリ起動、終了、ファイルオープン、ファイル保存、ドキュメントの状態取得、アプリケーションの状態取得、ウィンドウの状態取得、印刷……と、これだけです。

プレビュー.appが持っている「プレビュー.appらしい機能」(インスペクタで表示される内容が取得できるとか、ソフトプルーフができるとか、注釈にアクセスできるとか、PDF保存時にQuartzフィルタをかけるとか)は何も呼び出せません。メニューやボタンをGUI Scripting経由で操作して無理矢理コントロールするほかありません。Apple純正アプリのAppleScript対応度としては「仕事をしているフリ」のレベルを出ない内容です。

ところが、プレビュー.appでPDFをオープン+保存できることには、それだけでもかなり意義があります。

さまざまなアプリケーションでPDFを加工して保存した場合に、PDFのファイルサイズが大きなままである場合があります。実際にSkimでPDFを加工処理したときに、このような症状に遭遇しました。自分で使う分にはあまりこだわりませんが、これをお客に納品する必要があるとか、納品する書類の素材として使うような場合には気になります。

切り抜きなどして元よりもファイルサイズが小さくなる「はず」だけれども加工前とファイルサイズが変わらない状態の(ちょっと状態に疑問が付く)PDFを、「プレビュー.app」でオープンして保存すると、この状態が是正されました。

今後、プレビュー.appが持つ豊富な機能がAppleScript側に順次解放されるかどうか、次期OSアップデート時の行方が気になります。待ちきれなければ、Skimを併用すれば詳細なPDFの情報取得などは問題なく行えますので、ぜひSkimを使ってください。

スクリプト名:プレビュー.appのプロパティを取得する
tell application “Preview”
  properties
  
–> {frontmost:false, class:application, name:”Preview”, version:”7.0″}
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:プレビュー.appにbundle IDでtellする
tell application id “com.apple.preview”
  properties
  
–> {frontmost:false, class:application, name:”Preview”, version:”7.0″}
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:プレビュー.appで指定PDFをオープン
set aFIle to choose file of type {“com.adobe.pdf”}

tell application “Preview”
  open aFIle
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:プレビュー.appでオープンした書類のプロパティを取得する
set aFIle to choose file of type {“com.adobe.pdf”}

tell application “Preview”
  open aFIle
  
  
tell document 1
    properties
    
–> {modified:false, path:”/Users/maro/Desktop/blazingshadow1_trial.pdf”, class:document, name:”blazingshadow1_trial.pdf”}
  end tell
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

スクリプト名:プレビュー.appでファイルをオープンして保存してクローズ
set aFIle to choose file of type {“com.adobe.pdf”}

set aDocDir to path to desktop folder

tell application “Preview”
  open aFIle
  
  
tell document 1
    set aName to name
  end tell
  
  
set newPath to (aDocDir as string) & aName
  
  
save document 1 in file newPath
  
  
close document 1
  
end tell

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に