Archive for 8月, 2012

2012/08/25 Keynoteで発表者ノートの一覧を取得する

Keynote(Keynote 09)でオープン中のKeynote書類に書かれている「発表者ノート」の一覧を取得するAppleScriptです。

keynote1.png

とりあえず、簡単にできることをメモしておいたぐらいの内容ですが、指定フォルダ以下のkeynote書類をすべてSpotlight(mdfind)で検出して、順次オープンして「書類名」「タイトル」「発表者ノート」などをいっさいがっさい集めるような、そういうAppleScriptに進化させていくのがよいでしょう。

スクリプト名:Keynoteで発表者ノートの一覧を取得する
tell application “Keynote”
  tell slideshow 1
    set sCount to count every slide
    
set nList to {}
    
    
repeat with i from 1 to sCount
      
      
tell slide i
        try
          set aNotes to notes
        on error
          set aNotes to “”
        end try
      end tell
      
      
set the end of nList to aNotes
      
      
    end repeat
  end tell
end tell

nList
–> {”いちまいめー”, “にまいめー”, “さんまいめー”, “よんまいめー”}

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

もっと短く書けました。わざわざループ展開しなくても大丈夫でした。

スクリプト名:Keynoteで発表者ノートの一覧を取得する v2
tell application "Keynote"
  tell slideshow 1
    set nList to notes of every slide
  end tell
end tell

nList
–> {"いちまいめー", "にまいめー", "さんまいめー", "よんまいめー"}

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

2012/08/25 Retina Displayの接続チェック v2

MacBook RetinaのようなRetina Display上で各種アプリケーションを表示すると、表示サイズが従来と変わるケースがある(iOSシミュレータとか)ので、Retina Displayの接続検知ルーチンを作ってあったのですが、処理内容があまりになげやりだったので、ちゃんと作ってみました。

MacBook Pro Retinaに非Retinaの外付けディスプレイをつないで、Retina対応アプリ(iOSシミュレータ)がどのように振る舞うか実験をしてみました。

ret3.png
▲Retina非対応のMacBook Airに外付けディスプレイを接続して、iOSシミュレータで「iPhone(Retina)」の画面を表示させてみたところ。普通、こんなふうに大きく表示される

ret1.png
▲MacBook Pro Retina上ではiOSシミュレータを「iPhone(Retina)」画面で表示させると小さく(高解像度で)表示される

ret2.png
▲外付けの非Retinaディスプレイにそのままウィンドウを移動させると、ドットサイズは変わらない。ただし、高解像度表示が解除になる

ret4.png
▲同じiOSシミュレータ(iPhone(Retina)表示)の画面を、MacBook Airからの外付けモニタ出力時と、MacBook Air Retinaの本体ディスプレイ上の出力時で比較したところ

スクリプト名:Retina Displayの接続チェック v2
set rRes to chkRetinaDisplay() of me
–> true –> Retina Displayが接続されているとtrueが、接続されていないとfalseが返る

–Retina Displayの接続チェック v2
on chkRetinaDisplay()
  
  
try
    set aRes to do shell script “/usr/sbin/system_profiler SPDisplaysDataType | grep Retina”
    
if aRes contains “Yes” then
      return true
    else
      return false
    end if
  on error
    return false
  end try
  
end chkRetinaDisplay

(*

– system_profiler SPDisplaysDataType の出力結果から
– Retina Display に関する箇所を抜粋

Displays:
Color LCD:
Display Type: LCD
Resolution: 2880 X 1800
Retina: Yes
Pixel Depth: 32-Bit Color (ARGB8888)
Main Display: Yes
Mirror: Off
Online: Yes
Built-In: Yes
Connection Type: DisplayPort

*)

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

2012/08/22 OmniGraffleで日本語版と英語版の画面キャプチャを並べた書類を作成する

2012/08/22 指定フォルダ内のPDFの空白ページ以外を個別に取り出して別フォルダに保存 v3

指定フォルダ内のPDFの空白ページ「以外」を個別に取り出して、別フォルダに保存するAppleScriptです。

HTMLコンテンツを大量にプリンタで印刷するのに、HTMLレンダラー「wkpdf」を用いて個別のPDFに書き出し、これを連結してまとめてプリンタに送ろうとしたときに必要で作ったものです。

wkpdfでレンダリングしたときに余白ができて、内容が2ページ目まであふれていないのに空白の2ページ目が作られるケースがありました。これに対策すべく……

(1)PDFのページ数を数える。1ページのPDFなら、出力先フォルダにそのまま(ページ数を振りつつ)コピー

(2)2ページ以上のPDFなら、skimでPDFをオープンして、各ページに存在する文字情報を取得。2ページ目以降に文字情報が存在していない場合には、グラフィックが存在している可能性があるため、個別にPDFに書き出して、Photoshopでオープンし画像のヒストグラムを取得。画像のヒストグラムにドットの反応がなければ、「真っ白なページだった」=「空白ページ」とみなして破棄。ドットの反応があれば、出力先フォルダに(ページ数を振りつつ)コピー

という動作を行います。

この前のバージョンでは、2ページ目への文字あふれしか検出できていなかったのですが、改良してグラフィック要素があふれた場合でも対応できるようにしました。

実戦投入して、便利に使えています。

なお、おおきなおともだちの皆様にはお分かりと思いますが、Photoshop CS3以降でなくともPhotoshop Elements(お安い)でも代用できます。

スクリプト名:指定フォルダ内のPDFの空白ページ以外を個別に取り出して別フォルダに保存 v3
set inFol to choose folder with prompt “処理対象のPDFが入っているフォルダを指定”

set dtPath to choose folder with prompt “出力先フォルダを選択”
set dtPathStr to dtPath as string

tell application “Finder”
  tell folder inFol
    set aFileList to (every file whose name ends with “.pdf”) as alias list
  end tell
end tell

–それぞれのPDFの1ページ目のみ抽出
repeat with i in aFileList
  set j to contents of i
  
set pNum to pdfCount(j) of me
  
if pNum = 1 then
    –ファイルをコピー、ファイルのリネーム?
    
tell application “Finder”
      set fRes to duplicate j to dtPath
      
set tmpName to name of fRes
      
      
set bName to retFileNameWithoutExt(tmpName) of me
      
set cName to bName & “_1.pdf”
      
      
set name of fRes to cName
    end tell
    
    
  else if pNum > 1 then
    –1ページ目以外のページを、空白でないかどうかチェックする
    
set outList to {1}
    
    
repeat with ii from pNum to 2 by -1
      set aRes to detectBlankPageFromPDF(j, ii) of me
      
if aRes = false then
        set the end of outList to ii
      end if
    end repeat
    
    
    
–空白でないページのみ切り出して出力
    
repeat with ii in outList –出力対象ページ番号が入ったリスト
      set jj to contents of ii
      
set aRes to trimPDFbyPage(j, dtPathStr, jj) of me –エラー時にはfalseが返る
      
    end repeat
    
    
  end if
end repeat

–PDFの指定ページのみトリミング
on trimPDFbyPage(aFile, outFol, pageNum)
  tell application “Skim”
    close every document
    
    
open aFile
    
    
tell document 1
      set cCount to count every page
      
if cCount < pageNum then return false
    end tell
    
    
tell document 1
      set aProp to properties
      
set aRes to info of aProp
      
set aName to file name of aRes
      
set bName to retFileNameWithoutExt(aName) of me
      
      
set cName to bName & “_” & (pageNum as string) & “.pdf”
      
      
tell page pageNum
        set bList to bounds
        
set anImage to grab for bList –type “PDF”
      end tell
    end tell
    
    
set target_file to outFol & cName
    
write_to_file(anImage, target_file, false) of me
    
    
close every document
    
  end tell
end trimPDFbyPage

–ファイル名から拡張子を外す
on retFileNameWithoutExt(fileNameStr)
  set fLen to length of fileNameStr
  
set revText to (reverse of (characters of fileNameStr)) as string –逆順テキストを作成
  
set anOffset to offset of “.” in revText
  
set fRes to text 1 thru (fLen - anOffset) of fileNameStr
  
return fRes
end retFileNameWithoutExt

–指定PDFのページ数をかぞえる
on pdfCount(aPDF)
  return ((do shell script (“ruby -e \”require ‘osx/cocoa’;include OSX;require_framework ‘Quartz’;print PDFDocument.alloc.initWithURL(NSURL.fileURLWithPath(” & (quoted form of POSIX path of aPDF) & “)).pageCount\”")) as number)
end pdfCount

on makeFN(aNum, aDigit)
  set aText to “00000000000″ & (aNum as text)
  
set aLen to length of aText
  
set aRes to text (aLen - aDigit) thru -1 of aText
  
return aRes
end makeFN

–ファイルの追記ルーチン「write_to_file」
–追記データ、追記対象ファイル、boolean(trueで追記)
on write_to_file(this_data, target_file, append_data)
  try
    set the target_file to the target_file as text
    
set the open_target_file to open for access file target_file with write permission
    
if append_data is false then set eof of the open_target_file to 0
    
write this_data to the open_target_file starting at eof
    
close access the open_target_file
    
return true
  on error error_message
    try
      close access file target_file
    end try
    
return error_message
  end try
end write_to_file

–指定PDFの指定ページが空白(真っ白)かどうか判定する
on detectBlankPageFromPDF(aFile, targPage)
  tell application “Skim”
    close every document
    
    
open aFile
    
    
tell document 1
      set cCount to count every page
      
set cDigit to length of (cCount as string)
      
      
set docName to name
      
      
tell page targPage
        set bList to bounds
      end tell
      
      
if cCount > 1 then
        
        
–指定ページに文字が存在するかチェック
        
tell page targPage
          set aProp to properties
          
set aText to first item of text of aProp
        end tell
        
        
if aText is not equal to “” then
          return false
        end if
      end if
      
      
tell page targPage
        set anImage to grab for bList –type “PDF”
      end tell
    end tell
    
    
close every document
    
  end tell
  
  
–指定ページ内容をテンポラリフォルダに書き出す
  
set outFol to (path to temporary items from system domain) as string
  
set docName to (do shell script “/usr/bin/uuidgen”) & “.pdf”
  
  
set target_file to outFol & docName
  
write_to_file(anImage, target_file, false) of me
  
  
do shell script “sync” –キャッシュされたままディスクに書かれていないことがあるので実行
  
  
  
–書き出したPDFをPhotoshopでオープンしてヒストグラムから描画物の存在を検出
  
–Photoshop Elementsでも代用可
  
tell application “Adobe Photoshop CS3″
    close every document saving no
    
open file target_file
    
tell current document
      set aList to histogram
    end tell
    
close every document saving no
  end tell
  
  
–テンポラリPDFを削除する
  
do shell script “/bin/rm -f “ & (quoted form of POSIX path of target_file)
  
  
set aRes to detectWhiteFromList(aList) of me
  
  
return aRes –空白だとtrueを、そうでない場合にはfalseを返す
  
end detectBlankPageFromPDF

–与えられたPhotoshopヒストグラム(256段階の明るさのピクセル数)から、その内容が真っ白かどうかを検出
on detectWhiteFromList(aList)
  set aLen to length of aList
  
  
set zeroF to true
  
repeat with i from 1 to (aLen - 1) –真っ白かどうかを検知している
    if item i of aList is not equal to 0 then
      set zeroF to false
      
exit repeat
    end if
  end repeat
  
  
return zeroF
  
end detectWhiteFromList

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

2012/08/22 Retina Displayの接続チェック

MacBook Pro Retina Displayの上でさまざまなツールを動かすと、従来のディスプレイ上で動かすのと挙動が違ってなかなか焦るものがあります。

たとえば、画像を表示したりiOSシミュレーターを起動したりすると、画面上のサイズが同じでも高解像度で表示されたり、ぜんぜん動きが違います。

そこで、仕方なくRetina Display搭載マシンかどうかを調べるAppleScriptを作ってみました。原理は簡単で、System Profilerでディスプレイの情報を取得し、Retinaの文字を探してみるというものです。

retina.png

スクリプト名:15インチMacBook Pro Retina本体ディスプレイで画面解像度を取得する
–15インチMacBook Pro Retina本体ディスプレイ
set aRes to retDisplayResolution() of me
–> {{2880, 1800}}

–モニタ解像度を取得。複数モニタ接続時にはリストで列挙
on retDisplayResolution()
  
  
set sRes to do shell script "/usr/sbin/system_profiler SPDisplaysDataType | grep Resolution"
  
  
set sList to paragraphs of sRes
  
set resList to {}
  
  
repeat with i in sList
    set j to contents of i
    
set x1 to word 2 of j as number
    
set y1 to word 4 of j as number
    
set the end of resList to {x1, y1}
  end repeat
  
  
return resList
  
end retDisplayResolution

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

Retina = trueのディスプレイはRetinaの項目があるものの、非RetinaディスプレイでRetina = falseの記述にはまだ(いまのところ)お目にかかっていません。そのうち、作り直すことになるかもしれませんが……いまのところは、本体内蔵のディスプレイしかRetina Displayが存在していないのでこんなもんでしょう。

スクリプト名:Retina Displayの接続チェック
set rRes to chkRetinaDisplay() of me
–> true –> Retina Displayが接続されているとtrueが、接続されていないとfalseが返る

–Retina Displayの接続チェック
on chkRetinaDisplay()
  
  
try
    set aRes to do shell script “/usr/sbin/system_profiler SPDisplaysDataType | grep Retina”
  on error
    return false
  end try
  
  
return true
  
end chkRetinaDisplay

(*

– system_profiler SPDisplaysDataType の出力結果から
– Retina Display に関する箇所を抜粋

Displays:
Color LCD:
Display Type: LCD
Resolution: 2880 X 1800
Retina: Yes
Pixel Depth: 32-Bit Color (ARGB8888)
Main Display: Yes
Mirror: Off
Online: Yes
Built-In: Yes
Connection Type: DisplayPort

*)

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

2012/08/07 iPhoneアプリをAppleScriptからコントロール

iPhoneアプリ(iOSアプリ)をAppleScriptからコントロールできるメドが立ちました。実際にAppleScriptからコントロールできています。

最初にお断りしておきますが……これは、「iPhoneアプリがiPhone実機で動いているところをAppleScript経由でコントロールする」という話ではありません。だいたい、AppleEventの仕組みが存在しないiOSに対してOS X側からネットワークごしにリモートAppleEventを投げ、応答があるわけがありません。

iphone11.jpg

Mac OS X上で稼働するiOSシミュレータ上で動くアプリをAppleScriptからコントロールした、ということです。確認はOS X 10.8+Xcode 4.4+iOSシミュレータ v5.1(272.21)で実施しました。

冷静になって考えれば、CocoaベースのアプリケーションであるiOSアプリが、特定のアプリ(iOSシミュレータ)の特定のウィンドウ上で稼働しているというのがiOSシミュレータの動作状態です(当然、例外はあると思います)。iOSアプリの作り方にもよりますし、普遍性がどの程度あるのかは保証のかぎりではありません。ただ、自分が関わっているiOSアプリケーションに対してコントロールを試みた、という話です。

この状態は、とくに変わったものではなく、一般的なCocoaアプリケーション内の1ウィンドウ内の各種GUI Elementに対してのアクセス……というとらえ方ができます。

とくに、技術的に困難な可能性はないと判断し、実際に試してみたら……当然のようにできました。

実際に、AppleScriptからiOSアプリケーションの画面上の情報を取得したり、特定のボタンをタップしたり……という操作を行えています(値の設定はかなり無理)。

自分としては、開発中の大規模なiOSアプリの画面スナップショット(画面数が多いので仕様書用の画像作成が大変)をひととおり自動作成するような自動処理を考えています。当然、書き出したあとは仕様書の中に貼り込んだ画面を最新のものにAppleScriptで自動で貼り替えることになります。このあたりは、日常茶飯事です。

よくも悪くも、「最高到達点」がその程度の処理内容にすぎないので、iOSアプリの開発資料の作成の手間を減らしたいとか、そういう「差し迫った必要性」がなければ、ほとんどのMac/iPhoneユーザーには関係のない話ではあります。

ただし、アプリ上のひととおりの画面のスナップショットを、仕様書上の画面番号を付けて指定フォルダに自動で保存できるとなれば、これは相当に便利です。

追記;予定していた仕様のものは、ひととおりできました。AppleScriptObjCでさらっと作って、便利に使えそうです。