Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

カテゴリー: file

クリップボード内のテキストをSayコマンドで読み上げて音声ファイル化

Posted on 5月 27, 2020 by Takaaki Naganoya

読み上げ対象のテキストをコピーした状態で実行するAppleScriptです。クリップボード内のテキストをSayコマンドで読み上げて、音声ファイルにレンダリングします。出力後、読み上げ所要時間を出力ファイルから求めてダイアログ表示します。

音声レンダリング処理は実際の音声読み上げ処理よりも短い時間で完了します。

–> Downlad Script With library within its bundle

掲載のリストを実行しても、スライダー入力ライブラリが含まれていないため、そのままでは実行できません。↑のScriptをまるごとダウンロードして展開すると、ライブラリ入りのScriptになります。実行にはダウンロードしたScriptをご利用ください。プログラムリスト掲載は参考のために行なっているものです。


▲ステップ1:念のために、読み上げ対象テキストをダイアログ表示


▲ステップ2:読み上げ時の速度をスライダーで選択


▲ステップ3:読み上げ音声を選択


▲ステップ4:読み上げ所要時間をダイアログ表示


▲ステップ5:音声レンダリングしたファイルをQuickTime Playerでオープン

AppleScript名:クリップボード内のテキストをSayコマンドで読み上げて音声ファイル化.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/27
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "AVFoundation"
use scripting additions
use slLib : script "sliderLib"

property |NSURL| : a reference to current application’s |NSURL|
property NSDate : a reference to current application’s NSDate
property NSUUID : a reference to current application’s NSUUID
property NSFileManager : a reference to current application’s NSFileManager
property AVAudioPlayer : a reference to current application’s AVAudioPlayer
property NSDateFormatter : a reference to current application’s NSDateFormatter
property NSSpeechSynthesizer : a reference to current application’s NSSpeechSynthesizer

set aInfo to clipboard info
set uCount to (clipboard info for Unicode text)
if uCount = {} then
  display dialog "There is no text information in the clipboard" with title "Terminate information" buttons {"OK"} default button 1 with icon 2
  
return
end if

set totalCount to item 2 of item 1 of uCount

set aStr to the clipboard as text

–読み上げ内容の確認
display dialog aStr with title "Text length:" & (uCount as string) & " chars."

–読み上げ速度をSliderで入力
set rRes to slLib’s chooseBySlider(180, 220, "Select TTS reading pitch (small number:slow)")

–読み上げTTSキャラクタの選択
set aLoc to (current application’s NSLocale’s currentLocale()’s identifier()) as string –>  "ja_JP"
set vList to getTTSVoiceNameWithLanguage(aLoc) of me
set vRes to choose from list vList with prompt ("Select TTS Voice in your language :" & aLoc) without empty selection allowed
if vRes = false then return

set vCharacter to contents of first item of vRes

–音声ファイルの作成先パスを求める
set aUUID to NSUUID’s UUID()’s UUIDString() as string
set aPath to (((path to movies folder) as string) & aUUID & ".aif")
set aPOSIX to POSIX path of aPath

–音声レンダリング
tell current application
  say aStr using vCharacter saving to (aPOSIX) speaking rate rRes without waiting until completion
end tell

–レンダリングした音声の読み上げ所要時間を計算
set aDur to getDuration(aPath as alias) of me

–レンダリングした音声ファイルをオープン
tell application "QuickTime Player"
  open aPath
end tell

–完了報告
display dialog "読み上げ所要時間:" & my formatHMS(aDur)

on getTTSVoiceNameWithLanguage(voiceLang)
  set outArray to current application’s NSMutableArray’s new()
  
  
set aList to NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aDIc to (NSSpeechSynthesizer’s attributesForVoice:j)
    (
outArray’s addObject:aDIc)
  end repeat
  
  
set aPredicate to current application’s NSPredicate’s predicateWithFormat_("VoiceLocaleIdentifier == %@", voiceLang)
  
set filteredArray to outArray’s filteredArrayUsingPredicate:aPredicate
  
set aResList to (filteredArray’s valueForKey:"VoiceName") as list
  
  
return aResList
end getTTSVoiceNameWithLanguage

on getDuration(aFile)
  set aURL to |NSURL|’s fileURLWithPath:(POSIX path of aFile)
  
  
repeat 1000 times
    set aAudioPlayer to AVAudioPlayer’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
    
set aRes to aAudioPlayer’s prepareToPlay()
    
if aRes as boolean = true then exit repeat
    
delay 0.5
  end repeat
  
  
set channelCount to aAudioPlayer’s numberOfChannels()
  
set aDuration to aAudioPlayer’s duration()
  
return aDuration as real
end getDuration

on retAvailableTTSnames()
  set outList to {}
  
  
set aList to NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aInfo to (NSSpeechSynthesizer’s attributesForVoice:j)
    
set aInfoRec to aInfo as record
    
set aName to VoiceName of aInfoRec
    
set the end of outList to aName
  end repeat
  
  
return outList
end retAvailableTTSnames

on formatHMS(aTime)
  set aDate to NSDate’s dateWithTimeIntervalSince1970:aTime
  
set aFormatter to NSDateFormatter’s alloc()’s init()
  
  
—This formatter text is localized in Japanese.
  
if aTime < hours then
    aFormatter’s setDateFormat:"mm分ss秒"
  else if aTime < days then
    aFormatter’s setDateFormat:"HH時間mm分ss秒"
  else
    aFormatter’s setDateFormat:"DD日HH時間mm分ss秒"
  end if
  
  
set timeStr to (aFormatter’s stringFromDate:aDate) as string
  
return timeStr
end formatHMS

★Click Here to Open This Script 

Posted in dialog file Language Locale Text | Tagged 10.13savvy 10.14savvy 10.15savvy AVAudioPlayer NSDate NSDateFormatter NSFileManager NSMutableArray NSPredicate NSSpeechSynthesizer NSURL NSUUID | Leave a comment

Finder上で「すべてのファイル名拡張子を表示」にチェックが入っているかを返す

Posted on 5月 19, 2020 by Takaaki Naganoya

Finderの環境設定で「すべてのファイル名拡張子を表示」にチェックが入っているかを確認するAppleScriptです。

iWorkアプリケーション(Keynote、Pages、Numbers)では、オープン中の書類名を返すときに、Finderの上記の環境設定の内容を反映して拡張子を含めたり、含めなかったりしつつ名称を返すという仕様。

「ファイル名単独で求めることなんてないでしょ?」と思いつつも、オープン中のドキュメントの存在確認を名称で行うため、けっこう左右されることになります。

AppleScript名:Finder上で「すべてのファイル名拡張子を表示」にチェックが入っているかを返す.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/05/19
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

set exRes to checkDisplayTheFileExtensionsInFinder() of me
–> true

on checkDisplayTheFileExtensionsInFinder()
  try
    set aRes to (do shell script "/usr/bin/defaults read NSGlobalDomain AppleShowAllExtensions") as integer
    
return aRes as boolean
  on error
    return false
  end try
end checkDisplayTheFileExtensionsInFinder

★Click Here to Open This Script 

Posted in file shell script | Tagged Finder Keynote Numbers Pages | 2 Comments

指定画像をICNSに変換

Posted on 3月 19, 2020 by Takaaki Naganoya

指定画像をICNS書類に変換するAppleScriptです。

ICNS書類自体は、Classic Mac OS時代にあったアイコンの画像リソースである「icn#」リソースをファイル化したもの、と理解しています(位置付け的に、データ形式とか厳密なレベルで比較したわけではなくて)。

現在では、ICNS書類を作ってアプリケーションアイコンに指定したり、

Xcode上で解像度の異なる画像ファイルをドラッグ&ドロップすると、アプリケーションのICNSファイルを作ってくれます。

Finder上で解像度の異なる画像をフォルダ構造に入れてフォルダの拡張子をつけかえるだけでICNSと認識してくれたような記憶もあります。

そのため、ICNS書類をわざわざScriptから作る需要もないだろうと考えて掲載してこなかったのですが、意外なところでICNS書類の作成を求められました。

自作アプリケーションのカスタム書類を定義して、書類にカスタムアイコンをつけようとしたところ、PNGなどの画像では認識されず、結局ICNS書類を指定する必要に迫られました(意外)。

そんなわけで、いろいろICNS作成Scriptをまとめておきました。

こちらのScriptは実際には実行していませんが、/usr/bin/tiff2icnsコマンドがmacOS 10.15にも含まれていることを確認しています。

AppleScript名:TIFF画像をicnsに変換 v2scpt
tell application "Finder"
  set a to choose file
end tell
set theTiffPath to POSIX path of a
–128 x 128のtiff画像が必要
do shell script "/usr/bin/tiff2icns -noLarge " & (quoted form of theTiffPath)

★Click Here to Open This Script 

こちらは、オープンソースの「IcnsFactory」をフレームワーク化したIconFactory.frameworkを呼び出すバージョンです。自分が実際に使ったのはこちらです。

macOS 10.14以降ではScript Debugger上で実行するか、Script Debuggerから書き出したアプレット(Application(Enhanced))で実行するか、あるいはMacのSIPを解除して実行するなどの方法で実行できます。

Xcode上のAppleScript Applicationプロジェクトに突っ込んで呼び出せば、SIPの解除やScript Debuggerのインストールは必要ありません。

–> Download iconFactory.framework

AppleScript名:画像をICNS書類に
— Created 2017-04-14 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "IconFactory" –https://github.com/kgn/IcnsFactory

set aFile to POSIX path of (choose file)
set outFile to POSIX path of (choose file name)
set anImage to current application’s NSImage’s alloc()’s initWithContentsOfFile:aFile
set iconFam to current application’s IcnsFactory’s writeICNSToFile:outFile withImages:anImage

★Click Here to Open This Script 

Posted in file Image | Tagged 10.13savvy 10.14savvy 10.15savvy | Leave a comment

指定Bundle IDのstringsファイル中における指定タイトルの指定言語のローカライズ版を求める

Posted on 2月 24, 2020 by Takaaki Naganoya

/Applicationsフォルダ以下のすべてのアプリケーションのバンドル中のstringsファイル中を走査して、指定の言語における指定キーワード(”Quit”など)の指定言語におけるローカライズした文字列を取得するAppleScriptです。

自分でアプリケーションのローカライズを行う際に、既存のmacOS用のアプリケーションのバンドル内にあるstringsファイルを参考にするためのツールです。

もともとは、Shane StanleyがLate Night Softwareのフォーラムに投稿したプログラムですが、自分もいろいろ試していたように、アプリケーション側のローカライズのされ方(言語別のフォルダ名の名称指定)に「ゆらぎ」があるため、そのあたりは総当たりでテストしているようです。

オリジナルではデータが存在しない場合にはエラーにしていましたが、このようにすべてのアプリケーションに対してループで処理を行うような場合にはエラーで止まっては困るので、そのあたりを書き換えています。

/Applicationsフォルダ以下のSpotlight検索によるアプリケーションの取得についてはMetadata Libを用いています。自分の開発環境で1キーワード(x バンドル内のすべてのstringsファイル x すべてのアプリケーション)の問い合わせに3.6〜5秒程度かかっています。HDDの環境ではこれより大幅に長くかかるはずなので、ちょっと考えたくありません。

また、Stringsファイルで供給されるローカライズ情報については探すことができますが、OS側が強制的に供給するWindowメニューなどの内容については本Scriptで調査することはできません。ねんのため。

AppleScript名:指定Bundle IDのstringsファイル中における指定タイトルの指定言語のローカライズ版を求める.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/02/24
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5" — El Capitan (10.11) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
use mdLib : script "Metadata Lib" version "2.0.0" –https://macosxautomation.com/applescript/apps/Script_Libs.html

property NSWorkspace : a reference to current application’s NSWorkspace

set targStr to "Performance"
set origLang to "en" –From
set targLang to "hu" –To

set tList to {}

set origPath to POSIX path of (path to applications folder)
set aResList to mdLib’s searchFolders:{origPath} searchString:("kMDItemContentType == %@") searchArgs:{"com.apple.application-bundle"}
repeat with i in aResList
  set aTitleRes to getAppGUITitleWithCurrentLocale(i, targStr, origLang, targLang) of me
  
if aTitleRes is not equal to missing value and aTitleRes is not in tList then
    set the end of tList to aTitleRes
  end if
end repeat

return tList
–> {"Előadás", "Teljesítmény"}

–指定Bundle IDのstringsファイル中における指定タイトルの現在実行中のロケールのローカライズ版を求める
on getAppGUITitleWithCurrentLocale(aPath, aTargetTitle, origLang, targLang)
  return my localizedStringFor:aTargetTitle inBundle:aPath destLang:targLang sourceLang:origLang
end getAppGUITitleWithCurrentLocale

–Bundle IDからアプリケーションのPathを返す
on retAppAbusolutePathFromBundleID(aBundleID)
  set appPath to NSWorkspace’s sharedWorkspace()’s absolutePathForAppBundleWithIdentifier:aBundleID
  
if appPath = missing value then return false
  
return appPath as string
end retAppAbusolutePathFromBundleID

–Original By Shane Stanley@Late Night Software
–Modified By Takaaki Naganoya
–https://forum.latenightsw.com/t/localizing-gui-scripts/2246
on localizedStringFor:baseString inBundle:aPOSIX destLang:destLangCode sourceLang:sourceLangCode
  set anURL to current application’s |NSURL|’s fileURLWithPath:aPOSIX
  
set theBundle to current application’s NSBundle’s bundleWithURL:anURL
  
if theBundle = missing value then return missing value
  
  
set sourceLangString to current application’s NSString’s stringWithString:sourceLangCode
  
set destLangString to current application’s NSString’s stringWithString:destLangCode
  
  
— get source strings values
  
set theURLs to theBundle’s URLsForResourcesWithExtension:"strings" subdirectory:"" localization:sourceLangString
  
if theURLs = missing value then return missing value
  
if theURLs’s |count|() < 2 and (sourceLangString’s containsString:"_") as boolean then
    — try stripping off country-specific part
    
set sourceLangString to sourceLangString’s substringToIndex:2
    
set theURLs to theBundle’s URLsForResourcesWithExtension:"strings" subdirectory:"" localization:sourceLangString
  end if
  
  
if theURLs’s |count|() < 2 then
    — try long name for localization
    
set sourceLangString to (current application’s NSLocale’s localeWithLocaleIdentifier:"en")’s localizedStringForLocaleIdentifier:sourceLangString
    
set theURLs to theBundle’s URLsForResourcesWithExtension:"strings" subdirectory:"" localization:sourceLangString
  end if
  
  
if theURLs’s |count|() < 2 then return missing value –error "No " & sourceLangCode & " localization found"
  
  
repeat with sourceURL in theURLs
    — skip unlocalized file
    
if not (sourceURL’s URLByDeletingLastPathComponent()’s lastPathComponent()’s isEqualToString:"Resources") as boolean then
      set theData to (current application’s NSData’s alloc()’s initWithContentsOfURL:sourceURL)
      
      
if theData is missing value then return — error "No " & sourceLangCode & " localization found"
      
set sourceDict to (current application’s NSPropertyListSerialization’s propertyListWithData:theData options:0 format:0 |error|:(missing value))
      
if sourceDict = missing value then return missing value
      
      
set theKey to (sourceDict’s allKeysForObject:baseString)’s firstObject()
      
      
if theKey is not missing value then
        set stringsFileName to sourceURL’s lastPathComponent()’s stringByDeletingPathExtension()
        
set localURL to (theBundle’s URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:destLangString)
        
        
if localURL is missing value and (destLangString’s containsString:"_") as boolean then
          — try stripping off country-specific part
          
set destLangString to (destLangString’s substringToIndex:2)
          
set localURL to (theBundle’s URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:destLangString)
        end if
        
        
if localURL is missing value then
          — try long name for localization
          
set destLangString to ((current application’s NSLocale’s localeWithLocaleIdentifier:"en")’s localizedStringForLocaleIdentifier:destLangString)
          
set localURL to (theBundle’s URLForResource:stringsFileName withExtension:"strings" subdirectory:"" localization:destLangString)
        end if
        
        
if localURL is missing value then return missing value — "No " & destLangCode & " localization found"
        
        
set theData to (current application’s NSData’s alloc()’s initWithContentsOfURL:localURL)
        
if theData is missing value then missing value — "No " & destLangCode & " localization found"
        
set destDict to (current application’s NSPropertyListSerialization’s propertyListWithData:theData options:0 format:0 |error|:(missing value))
        
set destValue to (destDict’s objectForKey:theKey)
        
        
–if destValue is not missing value then return {destValue as text, stringsFileName as text}
        
if destValue is not missing value then return destValue as text
        
return missing value
      end if
    end if
  end repeat
  
return missing value
end localizedStringFor:inBundle:destLang:sourceLang:

★Click Here to Open This Script 

Posted in file File path Language Locale Text | Tagged 10.14savvy 10.15savvy NSBundle NSData NSLocale NSPropertyListSerialization NSString NSURL NSWorkspace | Leave a comment

画像に円を塗る

Posted on 2月 11, 2020 by Takaaki Naganoya

作成した画像(NSImage)に円を塗るAppleScriptです。

最近、6角形の図形ばかり塗りつぶしていましたが、「円に変えるとどうなんだろう?」と考えて、本ルーチンを試作してみました。

ためしに、6角形ではなく円で塗りつぶしてみたものの、そんなにイケていない感じがします。

AppleScript名:画像に円を塗る.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/02/11
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions

property aRadius : 40

set aFile to POSIX path of (choose file name)
if aFile does not end with ".png" then
  set aFile to aFile & ".png"
end if

set aColor to current application’s NSColor’s redColor()

set aImage to current application’s NSImage’s alloc()’s initWithSize:{300, 300}

repeat with y from 0 to 300 by aRadius + 4
  repeat with x from 0 to 300 by aRadius + 4
    my drawCircleOnNSIMage(aImage, aRadius, x, y, aColor)
  end repeat
end repeat

set sRes to my saveNSImageAtPathAsPNG(aImage, aFile)

#  MARK: Call By Reference
on drawCircleOnNSIMage(aImage, aRadius, aXpos, aYpos, aColor)
  set aBezier to generateCircle(aRadius, aXpos, aYpos) of me
  (
aImage)’s lockFocus()
  
aColor’s |set|()
  
aBezier’s fill() –ぬりつぶし
  (
aImage)’s unlockFocus()
end drawCircleOnNSIMage

#  MARK: circleのBezier曲線を作成して返す
on generateCircle(theRadius, x, y)
  set aRect to current application’s NSMakeRect(x, y, theRadius, theRadius)
  
set aCirCle to current application’s NSBezierPath’s bezierPath()
  
aCirCle’s appendBezierPathWithOvalInRect:aRect
  
return aCirCle
end generateCircle

# NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to current application’s NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to current application’s NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(current application’s NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –成功ならtrue、失敗ならfalseが返る
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

Posted in file File path Image | Tagged 10.14savvy 10.15savvy NSBezierPath NSBitmapImageRep NSColor NSImage NSPNGFileType NSString | Leave a comment

画像の指定エリアを透明色塗りつぶし(矩形切り抜き)

Posted on 2月 3, 2020 by Takaaki Naganoya

指定画像の指定エリア(矩形)を透明色で塗りつぶすAppleScriptです。透明色で塗りつぶす=矩形で切り抜いたようなかっこうになります。

画像(NSImage)を指定色で塗りつぶすという処理はよく見かけるものですが、clearColor()(透明色)で塗りつぶすという処理を行うとどうなるかといえば…………何も起こりません。

透明色でNSImageを塗りつぶすという処理は、たいへんに「ありそう」な処理ですが、なかなか見つからない。

さんざん探し回ったのですが、自分には見つけられませんでした。ちょうどEdama2さんに相談してみたところ、こういう処理を探し出してもらえました(ありがとうございますー)。ただ、見つけてきた処理そのままでは思い通りの結果にならなかったので、修正が必要であったとのこと。

透明塗りつぶし(切り抜き)の指定座標はCocoaの座標系をそのまま利用しているので、左下が原点です。

NSBezierPathで切り抜き(透明色で塗りつぶし)できるといいのになーという感じではあります。

AppleScript名:画像の指定エリアを透明色塗りつぶし(矩形切り抜き)
— Created 2017-11-19 by Takaaki Naganoya
— Modified 2018-02-14 by Takaaki Naganoya
— Modified 2020-02-03 by Edama2 & Takaaki Naganoya
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSUUID : a reference to current application’s NSUUID
property NSColor : a reference to current application’s NSColor
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSScreen : a reference to current application’s NSScreen
property NSBezierPath : a reference to current application’s NSBezierPath
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSCompositeClear : a reference to current application’s NSCompositeClear
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

–切り抜きエリア(複数)
set drawList to {{origin:{x:0, y:0}, |size|:{width:200, height:100}}, {origin:{x:100, y:100}, |size|:{width:50, height:50}}}

set imgPath to POSIX path of (choose file of type {"public.image"})
set anImage to NSImage’s alloc()’s initWithContentsOfFile:imgPath

set fillColor to (NSColor’s colorWithCalibratedRed:1.0 green:0.0 blue:0.0 alpha:0.9)

–透明色塗りつぶし処理呼び出し
set resImage to drawImageWithClearColorFill(anImage, drawList) of me

set aUUIDstr to (NSUUID’s UUID()’s UUIDString()) as string
set aPath to ((NSString’s stringWithString:imgPath)’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:"png"

set fRes to saveImageRepAtPathAsPNG(resImage, aPath) of me

on drawImageWithClearColorFill(anImage, drawList)
  set retinaF to (NSScreen’s mainScreen()’s backingScaleFactor()) as real
  
–>  2.0 (Retina) / 1.0 (Non Retina)
  
  
set aClearCol to NSColor’s clearColor()
  
set op to current application’s NSCompositeClear
  
  
anImage’s lockFocus() –描画開始
  
  
repeat with i in drawList
    set origX to (x of origin of i) / retinaF
    
set origY to (y of origin of i) / retinaF
    
set sizeX to (width of |size| of i) / retinaF
    
set sizeY to (height of |size| of i) / retinaF
    
    
–Draw Clear Color
    
–http://www.drissman.com/blog/archives/2009/10/09/nsrectfill_and_nscolor_clearcolor.html
    
set theRect to {{x:origX, y:origY}, {width:sizeX, height:sizeY}}
    
aClearCol’s |set|()
    
current application’s NSRectFillUsingOperation(theRect, op)
  end repeat
  
  
anImage’s unlockFocus() –描画ここまで
  
  
return anImage –returns NSImage
end drawImageWithClearColorFill

–画像を指定パスにPNG形式で保存
on saveImageRepAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep
  
  
set pathString to NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
  
set myNewImageData to (aRawimg’s representationUsingType:(NSPNGFileType) |properties|:(missing value))
  
return (myNewImageData’s writeToFile:newPath atomically:true) as boolean
end saveImageRepAtPathAsPNG

★Click Here to Open This Script 

Posted in file File path Image | Tagged 10.14savvy 10.15savvy NSBitmapImageRep NSColor NSCompositeClear NSImage NSPNGFileType NSScreen NSString NSUUID | 1 Comment

BlogアーカイブのMarkdown書類をリネーム。親、親+1階層フォルダをMM, YYYYとみなして反映

Posted on 1月 19, 2020 by Takaaki Naganoya

Finderで選択中のファイルすべてに対して、親フォルダ名、親+1階層のフォルダ名を反映させたファイル名をつけるAppleScriptです。

Blogアーカイブ本を作るのに、こうした道具を作って使っています。一度書いておけば、2度目からは大幅に時間を短縮できます。最初のファイルから拡張子を取得してそれを後続のファイルすべてに適用するため、同一種類のファイルである必要があります。自分用のツールならではの手抜きといったところでしょうか。

AppleScript名:BlogアーカイブのMarkdown書類をリネーム。親、親+1階層フォルダをMM, YYYYとみなして反映 .scptd
— Created 2020-01-18 by Takaaki Naganoya
— 2020 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

–選択ファイルを取得
tell application "Finder"
  set aSel to selection as alias list –Select documents to Rename
end tell

set aCount to 10 –start
set aStep to 10
set aDigit to 3

–パス情報を取得
set aFirst to first item of aSel
set aPOSIX to POSIX path of aFirst
set aPathStr to (current application’s NSString’s stringWithString:aPOSIX)
set aDateComList to aPathStr’s pathComponents() –ディレクトリごとにパス情報を分割してリスト化
set aExt to aPathStr’s pathExtension() –拡張子

set aYear to (item -3 of aDateComList) as string –親の親フォルダ名
set aMonth to (item -2 of aDateComList) as string –親フォルダ名

–リネーム(数百項目を超える場合にはFinderでは遅すぎるのでNSFileManagerで処理)
tell application "Finder"
  repeat with i in aSel
    set aTEXT to numToZeroPaddingStr(aCount, aDigit, "0") of me –3 digit
    
set aName to aYear & aMonth & aTEXT & "." & aExt
    
set name of i to aName
    
set aCount to aCount + aStep
  end repeat
end tell

–整数の値に指定桁数ゼロパディングして文字列で返す
on numToZeroPaddingStr(aNum as integer, aDigit as integer, paddingChar as text)
  set aNumForm to current application’s NSNumberFormatter’s alloc()’s init()
  
aNumForm’s setPaddingPosition:(current application’s NSNumberFormatterPadBeforePrefix)
  
aNumForm’s setPaddingCharacter:paddingChar
  
aNumForm’s setMinimumIntegerDigits:aDigit
  
  
set bNum to current application’s NSNumber’s numberWithInt:aNum
  
set aStr to aNumForm’s stringFromNumber:bNum
  
  
return aStr as text
end numToZeroPaddingStr

★Click Here to Open This Script 

Posted in file File path list Markdown Text | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy Finder NSNumber NSNumberFormatter NSNumberFormatterPadBeforePrefix NSString | Leave a comment

macOS 10.14にchoose fileのバグ?

Posted on 12月 13, 2019 by Takaaki Naganoya

macOS史上最低、どうしてあの状態でリリースしたのか理解に苦しむmacOS 10.13の後なので、どうしてもかなり贔屓目に見てしまうmacOS 10.14ですが、やはりバグはありました。

2019/12/13追記 デフォルト設定値の問題で、階層表示は行えることを確認しました。

choose file with showing package contents

このコマンドを実行した場合には、パッケージ内容を表示しつつパッケージ内のファイルを選択できるはずですが、macOS 10.14ではこれがパッケージ内のファイルは選択できるものの、階層表示できていません。こんなchoose file with showing package contentsの挙動なんて見たことがありません。


▲macOS 10.13.6上の動作


▲macOS 10.14.6上の動作。一応、バンドルを選択するとその内部のフォルダに移動することは可能だが、階層表示されない


▲macOS 10.15.2上の動作

追記 edama2さんから「こちらではそういうことはないよ?」というツッコミを送っていただきました。再度確認したところ、choose fileダイアログの項目分けの初期値がmacOS 10.14では「なし」ではなく「名前」になっているために、自分のデフォルト設定では階層表示されていなかったようです。


▲自分のmacOS 10.14.6環境の初期状態。項目分けが「名前」になっていた


▲自分のmacOS 10.14.6環境でも、項目分けを「なし」に変更すると階層表示された

Posted in Bug dialog file | Tagged 10.14savvy | Leave a comment

PDFフォームに入力して別名保存 v2

Posted on 12月 7, 2019 by Takaaki Naganoya

入力フォームつきのPDFにデータ入力して別名保存するAppleScriptです。

他のGUIアプリを操作してフォーム入力するのではなく、直接PDFKitの機能を利用してフォームPDFへの記入を行なっています。

テキスト入力フォーム、チェックボックス、コンボボックスへの入力に対処してみましたが、リストボックスの項目選択はまだ行えていません。


▲Adobe AcrobatでPDFフォームサンプルをオープンしたところ


▲本ScriptでサンプルフォームPDFを処理したところ


▲Script Debugger上で表示したPDFフォーム上の各種プロパティ。フォーム欄のタイプを名前ではなく各種属性値で判定したいところ

AppleScript名:PDFフォームに入力して別名保存(テキストフィールド、チェックボックス、コンボボックス)
— Created 2019-12-05 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Quartz"

property |NSURL| : a reference to current application’s |NSURL|
property PDFDocument : a reference to current application’s PDFDocument

set aHFSPath to (choose file of type {"com.adobe.pdf"} with prompt "Choose a PDF with Form")
set aPOSIX to POSIX path of aHFSPath
set aURL to (|NSURL|’s fileURLWithPath:aPOSIX)

set aPDFdoc to PDFDocument’s alloc()’s initWithURL:aURL
set pCount to aPDFdoc’s pageCount()
set firstPage to (aPDFdoc’s pageAtIndex:0)

set anoList to (firstPage’s annotations()) as list
if anoList is not equal to {missing value} then –指定PDF中にAnotationが存在する場合のみ処理
  repeat with i in anoList
    set aName to (i’s fieldName()) as string
    
    
set adicList to i’s annotationKeyValues
    
set aType to (adicList’s valueForKey:"/Type") as string
    
set aSubType to (adicList’s valueForKey:"/Subtype") as string
    
set aFormType to (adicList’s valueForKey:"/FT") as string
    
    
log {aType, aSubType, aFormType}
    
if aName ends with "Text Box" then –OK
      set aState to (i’s widgetStringValue()) as string
      
      
if aState = "" then
        (i’s setWidgetStringValue:"Piyomaru")
        
set bState to (i’s widgetStringValue()) as string
      end if
      
    else if aName ends with "Check Box" then –OK
      log {"Check Box"}
      
set aState to (i’s buttonWidgetState()) as boolean
      
if aState = false then
        (i’s setButtonWidgetState:true)
        
set bState to (i’s buttonWidgetState()) as boolean
      end if
      
    else if aName ends with "Combo Box" then
      log {"Combo Box"}
      
set tmpList to (i’s choices()) as list
      (
i’s setWidgetStringValue:(contents of some item of tmpList))
      
set cVal to (i’s widgetStringValue()) as string
      
log cVal
      
    else if aName ends with "List Box" then
      log {"List Box"}
      
set tmpList to (i’s choices()) as list
      
log {"tmpList", tmpList}
      
      
–(i’s setWidgetStringValue:(contents of some item of tmpList))
      
    end if
  end repeat
end if

–PDFの保存先のファイル名を入力させる(あらかじめパス文字列を作成しておいてもよい)
set newFileName to POSIX path of (choose file name with prompt "Input File name to save")
if newFileName does not end with ".pdf" then
  set newFileName to newFileName & ".pdf"
end if

–フォーム内容を変更したPDFを指定のパスに新規保存
set pdfRes to (aPDFdoc’s writeToFile:newFileName) as boolean

★Click Here to Open This Script 

Posted in file PDF | Tagged 10.13savvy 10.14savvy 10.15savvy NSURL PDFDocument | Leave a comment

CIFilterとGPUImageで画像差分検出演算を比較

Posted on 11月 22, 2019 by Takaaki Naganoya

オープンソースのGPUImage.framework(GPUImage1)はAppleScriptから呼び出すのも簡単ですし、機能セットも充実しており、非常に重宝して使っています。

ただし、GPUImage自体はSwiftで書き直されたGPUImage2、OSの変更を受けてOpenGLではなくMetalを用いるように書き直され、いまだ発展途上のGPUImage3と変化してきました。

・GPUImageのバージョンと搭載フィルタ数の推移

GPUImage1:■■■■■■■■■■■■ 125
GPUImage2:■■■■■■■■■■ 100
GPUImage3:■■■■ 45

GPUImage2/3とバージョンが進むにつれて搭載フィルタ数が減っており、かつGPUImage2以降はAppleScriptから呼べないため、GPUImage3は(自分の)移行先になりません。

Objective-CベースのGPUImage1系のプログラムを使い続けるという判断をしている開発者は多いようで、Github上で派生プログラムが大量にみつかります。ただ、GPUImage1系はmacOSがOpenGLを完全廃止にしたとたんに動かなくなるはずです。

このため、GPUImage1系に依存しているプログラムは別の部品を呼び出して処理する必要が出てきます。

一番有望なのは、macOS標準搭載のCIFilter。CIFilterについては、実際にプログラムを組んでみて「組みにくい」という印象を受けていました(機能はちゃんとしていそうなものの、機能数にドキュメントが見合っていません)。あと、AppleScriptからパラメータを指定しにくいという印象を受けていました(パラメータに指定するオブジェクトにAppleScriptから直接指定できないものがあるので、少しラッパーを書く必要がある)。

その後、各種フィルタを呼び出す汎用ルーチンを整備することで、利用難易度を下げる努力を自主的に行なっています。

・GPUImage3とCoreImageの搭載フィルタ数の比較

GPUImage3:■■■■ 45
CoreImage:■■■■■■■■■■■■■■■■■ 174

そこで、実際に処理速度の面でどうなのか、CIFilterとGPUImageを比較するとどうなのか、調べてみました。

まずは、ざっくりありもののルーチンを引っ張り出してきて実行。どちらも期待どおりの動作をしてくれます。ただし、周辺ルーチンの仕様が違っていたのでそこも合わせます。

乱数ファイル名を作成する定番のshell commandの「uuidgen」はお手軽な反面、呼び出すのに時間がかかるので(手元の時間で0.1秒程度強)、より高速なCocoaのUUIDStringを用いる部品に置き換えました。

それぞれのフレームワークごとの挙動の違いもあります。

経験上、GPUImage.frameworkは初回実行時だけは時間が長くかかる傾向があり(1秒以上かかる)、2回目以降はより短い時間で処理できるようになることを確認しています。一方、CIFilterは初回およびそれ以降の呼び出しでもさほど処理時間が変わりません。

これら2つを実際に同じデータで同一条件下で比較してみたところ、同一の処理(画像の差分計算)を行なったかぎりでは処理時間に差はほとんど有意な差はありませんでした。

Script Geek 2.0.1によるScriptの処理時間比較

First:GPUImage Version , Second:CoreImage Version

MacBookPro10,1,  macOS Version 10.14.6 (Build 18G2012),  10 iterations
         First Run   Total Time    Average  
First        0.969        1.194      0.119
Second       0.121        1.221      0.122
Ratio (excluding first run): 1:1.02

自分のメインマシン(MacBook Pro Retina 2012、Core i7 2.66GHz、RAM 8GB)ではどちらもテストデータ(1192x1684pixel 8bit color image x 2)を0.1〜0.2秒程度で処理できています。個人的には十分なスピードが出ていると感じていますが、不満がある方はデータをRAM Disk上に置いたり、並列処理するとよいのではないでしょうか(シングルスレッド処理が高速なマシンを用意するというのが一番効きそうではあります。iMac 5Kの上位モデルとか)。

すべての類似フィルタで比較を行なったわけではありませんが(どれとどれが類似、という情報を整理するのに手間がかかる)、一番使っている「空白検出のためのヒストグラム計算フィルタ」はGPUImageにしか存在しませんでした(作成当時の話で現在はCoreImageにもCIAreaHistogramなどの機能があります)。

空白検出処理を高速に行えることで巨大なメリットが生じています。用途については枚挙にいとまがありませんが、Markdown書類をPDFにレンダリングした場合に、末尾に無用な空白のページが生じるケースがあり、これを検出しつつ末尾のページを自動削除することで、膨大な無駄手間を削減できています。空白検出処理を高速に行う手段を用意していないと、Photoshopで指定ページをレンダリングしてヒストグラムを計算する必要があるわけで、Photoshopのない環境に進出できなくなります。

空白画像の検出処理はGPUImage.framework抜きで(AppleScriptだけで)2〜3倍高速な処理を行える部品「画像の空白判定 v3」を作成済みなので、GPUImageなしでもさほど困りません。


▲同一環境にて、Photoshop CC 2018、GPUImage、AppleScriptだけで処理している「画像の空白判定 v3」で各種サイズの画像の空白検出を実行(単位:秒)

Objective-C/Swift系の開発者の方々に「GPUImageを経由してGPUのパワーを使って処理するよりも、AppleScript単体で空白の比較演算処理したほうが速かった」という事実をお話しすると、最初は「嘘つけ」というひきつった笑顔の反応なのですが、実際にコードを動かして動作原理を丁寧に説明すると分かっていただけるようです。

–> Download dffFilterDemoPrograms(Two Code-Signed AppleScript Applet Executables with Framework in its bundle)

AppleScript名:CoreImageでCIDifferenceBlendMode.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/22
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use framework "QuartzCore"
use scripting additions

property CIFilter : a reference to current application’s CIFilter
property NSUUID : a reference to current application’s NSUUID
property |NSURL| : a reference to current application’s |NSURL|
property CIImage : a reference to current application’s CIImage
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

set mePath to POSIX path of (path to me)
set aImg to mePath & "Contents/Resources/book1_v2.0_0011.jpg"
set bImg to mePath & "Contents/Resources/book1_v2.1_0011.jpg"

–https://developer.apple.com/library/archive/documentation/
–GraphicsImaging/Reference/CoreImageFilterReference/index.html
–#//apple_ref/doc/filter/ci/CIDifferenceBlendMode
set cifilterName to "CIDifferenceBlendMode"

set imgRes to filterBlendImageFiles(aImg, bImg, cifilterName) of me

set outPath to retUUIDfilePath(POSIX path of (path to desktop), "png") of me
set sRes to saveNSImageAtPathAsPNG(imgRes, outPath) of me

–CIFilterをかけたJPEG画像を生成
on filterBlendImageFiles(aPath, bPath, aFilterName)
  –CIImage(A)を生成
  
set aURL to |NSURL|’s fileURLWithPath:aPath –Input
  
set aCIImage to CIImage’s alloc()’s initWithContentsOfURL:aURL
  
  
–CIImage(B)を生成
  
set bURL to |NSURL|’s fileURLWithPath:bPath –Input
  
set bCIImage to CIImage’s alloc()’s initWithContentsOfURL:bURL
  
  
— CIFilter をフィルタの名前で生成
  
set aFilter to CIFilter’s filterWithName:aFilterName
  
aFilter’s setDefaults() –各フィルタのパラメータはデフォルト
  
  
–Blend Filterを実行
  
aFilter’s setValue:(aCIImage) forKey:"inputImage"
  
aFilter’s setValue:(bCIImage) forKey:"inputBackgroundImage"
  
  
set aOutImage to aFilter’s valueForKey:"outputImage"
  
set outNSImage to convCIimageToNSImage(aOutImage) of me
  
return outNSImage
end filterBlendImageFiles

on retUUIDfilePath(aPath, aEXT)
  set aUUIDstr to (NSUUID’s UUID()’s UUIDString()) as string
  
set aPath to ((NSString’s stringWithString:aPath)’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT
  
return aPath
end retUUIDfilePath

on convCIimageToNSImage(aCIImage)
  set aRep to NSBitmapImageRep’s alloc()’s initWithCIImage:aCIImage
  
set tmpSize to aRep’s |size|()
  
set newImg to NSImage’s alloc()’s initWithSize:tmpSize
  
newImg’s addRepresentation:aRep
  
return newImg
end convCIimageToNSImage

on convNSImageToCIimage(aNSImage)
  set tiffDat to aNSImage’s TIFFRepresentation()
  
set aRep to NSBitmapImageRep’s imageRepWithData:tiffDat
  
set newImg to CIImage’s alloc()’s initWithBitmapImageRep:aRep
  
return newImg
end convNSImageToCIimage

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

AppleScript名:(比較用)GPUImageによる差分計算.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/22
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use scripting additions
use framework "Foundation"
use framework "AppKit"
use framework "GPUImage" –https://github.com/BradLarson/GPUImage

property NSUUID : a reference to current application’s NSUUID
property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSImage : a reference to current application’s NSImage
property NSPNGFileType : a reference to current application’s NSPNGFileType
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep

set mePath to POSIX path of (path to me)
set aImagePath to mePath & "Contents/Resources/book1_v2.0_0011.jpg"
set bImagePath to mePath & "Contents/Resources/book1_v2.1_0011.jpg"

set imgRes to composeImageWithBlendFilter(aImagePath, bImagePath, "GPUImageDifferenceBlendFilter") of me

set outPath to retUUIDfilePath(POSIX path of (path to desktop), "png") of me
set sRes to saveNSImageAtPathAsPNG(imgRes, outPath) of me

on composeImageWithBlendFilter(aImagePath, bImagePath, filterName)
  set aImage to NSImage’s alloc()’s initWithContentsOfFile:aImagePath
  
set bImage to NSImage’s alloc()’s initWithContentsOfFile:bImagePath
  
  
set aClass to current application’s NSClassFromString(filterName)
  
set blendFilter to aClass’s alloc()’s init()
  
set pictureA to current application’s GPUImagePicture’s alloc()’s initWithImage:aImage
  
pictureA’s addTarget:blendFilter
  
pictureA’s processImage()
  
set imgRes to blendFilter’s imageByFilteringImage:bImage
  
return imgRes
end composeImageWithBlendFilter

on retUUIDfilePath(aPath, aEXT)
  set aUUIDstr to (NSUUID’s UUID()’s UUIDString()) as string
  
set aPath to ((NSString’s stringWithString:aPath)’s stringByAppendingPathComponent:aUUIDstr)’s stringByAppendingPathExtension:aEXT
  
return aPath
end retUUIDfilePath

–NSImageを指定パスにPNG形式で保存
on saveNSImageAtPathAsPNG(anImage, outPath)
  set imageRep to anImage’s TIFFRepresentation()
  
set aRawimg to NSBitmapImageRep’s imageRepWithData:imageRep
  
set pathString to NSString’s stringWithString:outPath
  
set newPath to pathString’s stringByExpandingTildeInPath()
  
set myNewImageData to (aRawimg’s representationUsingType:(NSPNGFileType) |properties|:(missing value))
  
set aRes to (myNewImageData’s writeToFile:newPath atomically:true) as boolean
  
return aRes –true/false
end saveNSImageAtPathAsPNG

★Click Here to Open This Script 

Posted in file filter Image | Tagged 10.14savvy 10.15savvy CIFilter CIImage NSBitmapImageRep NSImage NSPNGFileType NSString NSURL NSUUID | Leave a comment

ファイルからのメタデータ情報の取得

Posted on 11月 21, 2019 by Takaaki Naganoya

指定ファイルからのメタデータ情報の取得を行うAppleScriptです。

メタデータの取得はshell commandの「mdls」で行えます。ほぼそれと同じ内容です。ループですぺての属性値を取得するか、属性ラベルを指定して個別に属性値を取り出すことになります。

この方法でメタデータの書き込みや追記が行えないかと調べてみましたが、どうもできないようで「自前でMDImporterを作れ」とか言われてもテーマが大きすぎて困ります……

メタデータの書き込みはshell commandのほうが手っ取り早そうです。

AppleScript名:ファイルからのメタデータ情報の取得 v1scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSMetadataItem : a reference to current application’s NSMetadataItem

set aFile to POSIX path of (choose file)
set aURL to |NSURL|’s fileURLWithPath:aFile

set aMetaInfo to NSMetadataItem’s alloc()’s initWithURL:aURL
set attrList to (aMetaInfo’s attributes()) as list

set metaList to {}

repeat with i in attrList
  set j to contents of i
  
set aTmpVal to (aMetaInfo’s valueForAttribute:(j))
  
set the end of metaList to {j as string, aTmpVal as {number, string, date, list}}
end repeat

return metaList

★Click Here to Open This Script 

AppleScript名:ファイルからのメタデータ情報の取得 v2a.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/11/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSMetadataItem : a reference to current application’s NSMetadataItem

set aFile to POSIX path of (choose file)
set aURL to |NSURL|’s fileURLWithPath:aFile

set aMetaInfo to NSMetadataItem’s alloc()’s initWithURL:aURL –fixed

–指定した属性ラベルの値だけ一括で取り出す
set metaDict to (aMetaInfo’s valuesForAttributes:{"kMDItemContentTypeTree", "kMDItemContentType"}) as record
–>
(*
(NSDictionary) {
  kMDItemContentTypeTree:{
    "com.apple.application-bundle",
    "com.apple.localizable-name-bundle",
    "com.apple.application-bundle",
    "public.directory",
    "public.executable",
    "com.apple.application",
    "public.item",
    "com.apple.package",
    "com.apple.bundle"
  },
  kMDItemContentType:"com.apple.application-bundle"
}
*)

★Click Here to Open This Script 

Posted in file Metadata | Tagged 10.14savvy 10.15savvy NSMetadataItem NSURL | Leave a comment

指定アプリケーションの指定ロケールのフォルダ内の該当キーワードを含むstringsファイル情報を抽出する

Posted on 11月 13, 2019 by Takaaki Naganoya

Bundle IDで指定したアプリケーションからすべてのLocaleを取得し、ダイアログ選択したLocale内のすべてのstringsファイルを読み込んでNSDictionary化して指定のキーワードを含むものを抽出するAppleScriptです。

指定キーワードが含まれていた場合には、

キー, 値, stringsファイルのパス

をリストで返します。調査のための下調べを行うものです。

AppleScript名:指定アプリケーションの指定ロケールのフォルダ内の該当キーワードを含むstringsファイル情報を抽出する
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSBundle : a reference to current application’s NSBundle
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSWorkspace : a reference to current application’s NSWorkspace
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants

set targID to "com.apple.Finder"
set targWords to "のコピー"

set bRes to getLocalizationsFromBundleID(targID) of me
–>  {"de", "he", "en_AU", "ar", "el", "ja", "en", "uk", "es_419", "zh_CN", "es", "da", "it", "sk", "pt_PT", "ms", "sv", "cs", "ko", "Base", "no", "hu", "zh_HK", "tr", "pl", "zh_TW", "en_GB", "vi", "ru", "fr_CA", "fr", "fi", "id", "nl", "th", "pt", "ro", "hr", "hi", "ca"}

–Select Localization
set curLang to first item of (choose from list bRes)

–Get All strings file path within a bundle
set aPath to retPathFromBundleID(targID) of me
set aPath to aPath & "/Contents/Resources/" & curLang & ".lproj"
set sList to getFilepathListByUTI(aPath, "com.apple.xcode.strings-text", "POSIX") of me

–Get Every Key & Value pair then filter target keyword
set aMDict to NSMutableDictionary’s new()
set hitList to {}
repeat with ii in sList
  set jj to contents of ii
  
set aDict to (NSDictionary’s alloc()’s initWithContentsOfFile:jj)
  (
aMDict’s addEntriesFromDictionary:aDict)
  
  
set kList to aMDict’s allKeys() as list
  
set vList to aMDict’s allValues() as list
  
  
set kCount to 1
  
repeat with i in vList
    set j to contents of i
    
if j contains targWords then
      set the end of hitList to {contents of item kCount of kList, j, jj}
    end if
    
set kCount to kCount + 1
  end repeat
  
end repeat

return hitList
–> {{"PW5_V2", "^0項目のコピーの準備中", "/System/Library/CoreServices/Finder.app/Contents/Resources/Japanese.lproj/Localizable.strings"}, …}

on getLocalizationsFromBundleID(aBundleID)
  set aRes to retPathFromBundleID(aBundleID) of me
  
if aRes = false then error "Wrong Bundle ID."
  
return getSpecifiedAppFilesLocalizationListWithDuplication(aRes) of me
end getLocalizationsFromBundleID

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を許容
on getSpecifiedAppFilesLocalizationListWithDuplication(appPOSIXpath)
  set aURL to (|NSURL|’s fileURLWithPath:appPOSIXpath)
  
set aBundle to NSBundle’s bundleWithURL:aURL
  
set locList to aBundle’s localizations()
  
return locList as list
end getSpecifiedAppFilesLocalizationListWithDuplication

on retPathFromBundleID(aBundleID)
  set aURL to NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
if aURL = missing value then return false –Error
  
return aURL’s |path|() as string
end retPathFromBundleID

on getFilepathListByUTI(aFolPOSIX, aUTI as string, aFileType as string)
  script spdFile
    property urlList : {}
  end script
  
  
if aFileType is not in {"file", "POSIX"} then return {}
  
  
set aFM to NSFileManager’s defaultManager()
  
set aFolExt to (aFM’s fileExistsAtPath:aFolPOSIX isDirectory:true) as boolean
  
if aFolExt = false then return {} –フォルダ自体が存在しなければヌルリストを返す
  
  
set aURL to |NSURL|’s fileURLWithPath:aFolPOSIX
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer)
  
set urlArray to (aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value))
  
if urlArray = missing value then return {}
  
  
set (urlList of spdFile) to urlArray as list
  
set newList to {}
  
  
repeat with i in (urlList of spdFile)
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, aUTI)
    
    
if utiRes is not equal to {} then
      if aFileType = "POSIX" then
        set the end of newList to j
      else if aFileType = "file" then
        set the end of newList to POSIX file j
      end if
    end if
    
  end repeat
  
  
return newList
end getFilepathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

★Click Here to Open This Script 

Posted in file File path UTI | Tagged 10.12savvy 10.13savvy 10.14savvy 10.15savvy Finder NSArray NSBundle NSDictionary NSDirectoryEnumerationSkipsHiddenFiles NSDirectoryEnumerationSkipsPackageDescendants NSFileManager NSMutableDictionary NSPredicate NSURL NSURLTypeIdentifierKey NSWorkspace | Leave a comment

指定フォルダ以下のすべてのファイルとフォルダ名から絵文字を除去する v2

Posted on 11月 5, 2019 by Takaaki Naganoya

指定フォルダ以下のすべてのファイル名とフォルダ名から絵文字を除去するAppleScriptです。Shane StanleyのremoveEmojiルーチンを使っています。

macOS 10.14.1で絵文字が大幅に追加されたため、これらの絵文字をファイル名に用いていた場合には10.14.1以下のバージョンのOS環境にそのままファイルを持っていくことができません。

 Zipアーカイブ → 展開時にエラー
 DiskImageにコピーするファイルを格納し、古いOSに持って行ってドライブとしてマウントしてファイルコピー → コピーできない(エラー)

という状態になります。絵文字自体に害はないのですが、規格がコロコロ変わる(追加される)ことで、ファイル名に用いるのには問題があるということでしょう。


▲もともとのファイル名、フォルダ名。絵文字を大量に使用している(普段はファイル名に絵文字は使っていません)


▲本Scriptで一括で処理したファイル名、フォルダ名。害のない1️⃣2️⃣3️⃣などの文字だけは残る

実際に作ってみたら、aliasに対するリネームはしょっちゅう行ってきたものの、POSIX pathを用いて指定フォルダ以下すべてをリネームするようなScriptは組んでいなかったので、ちょっと考えさせられました。


▲本Scriptでリネームして、CotEditorのScript PackをmacOS 10.13.6の環境に持っていけました。ただ、絵文字がないと寂しい感じがします

指定フォルダ以下のファイル/フォルダを一括取得するのに、今回はあえてSpotlightを使っていません。ファイルサーバー上のファイル/フォルダを処理する可能性がありそうなのと、外部ライブラリを使わないほうがよいと考え、このような構成になっています。

AppleScript名:指定フォルダ以下のすべてのファイルとフォルダ名から絵文字を除去する v2.scptd
—
—  Created by: Takaaki Naganoya
—  Created on: 2019/11/04
—
—  Copyright © 2019 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableArray : a reference to current application’s NSMutableArray
property NSRegularExpressionSearch : a reference to current application’s NSRegularExpressionSearch
property NSURLBookmarkResolutionWithoutUI : a reference to current application’s NSURLBookmarkResolutionWithoutUI

set aFol to POSIX path of (choose folder)

set anArray to NSMutableArray’s array()
set erArray to NSMutableArray’s array()
set aPath to NSString’s stringWithString:aFol
set dirEnum to NSFileManager’s defaultManager()’s enumeratorAtPath:aPath

repeat
  set aName to (dirEnum’s nextObject())
  
if aName = missing value then exit repeat
  
set aFullPath to aPath’s stringByAppendingPathComponent:aName
  
  
anArray’s addObject:aFullPath
end repeat

—逆順に(フォルダの深い場所&ファイル名から先に処理)
set revArray to (anArray’s reverseObjectEnumerator()’s allObjects()) as list

—リネーム
repeat with i in revArray
  set j to (NSString’s stringWithString:(contents of i))
  
set curName to j’s lastPathComponent() as string
  
set newName to removeEmoji(curName) of me
  
  
if curName is not equal to newName then
    set fRes to renameFileItem(j as string, newName) of me
    
if fRes = false then
      (erArray’s addObject:{j, newName})
    end if
  end if
end repeat

return erArray as list —リネームできなかったパス(フルパス、リネームするはずだった名称)

—絵文字除去
on removeEmoji(aStr)
  set aNSString to NSString’s stringWithString:aStr
  
return (aNSString’s stringByReplacingOccurrencesOfString:"[\\U0001F600-\\U0001F64F\\U0001F300-\\U0001F5FF\\U0001F680-\\U0001F6FF\\U00002600-\\U000026FF\\U00002700-\\U000027BF\\U0000FE00-\\U0000fE0F\\U0001F900-\\U0001F9FF\\U0001F1E6-\\U0001F1FF\\U00002B50-\\U00002B50\\U0000231A-\\U0000231B\\U00002328-\\U000023FA\\U000024C2-\\U000024C2\\U0001F194-\\U0001F194\\U0001F170-\\U0001F251\\U000025AB-\\U000025FE\\U00003297-\\U00003299\\U00002B55-\\U00002B55\\U00002139-\\U00002139\\U00002B1B-\\U00002B1C\\U000025AA-\\U000025AA\\U0001F004-\\U0001F004\\U0001F0CF-\\U0001F0CF]" withString:"" options:(NSRegularExpressionSearch) range:{0, aNSString’s |length|()}) as text
end removeEmoji

—ファイル/フォルダのリネーム
on renameFileItem(aPOSIXPath, newName)
  set theNSFileManager to NSFileManager’s defaultManager()
  
set POSIXPathNSString to NSString’s stringWithString:(aPOSIXPath)
  
  
–Make New File Path
  
set anExtension to POSIXPathNSString’s pathExtension()
  
set newPath to (POSIXPathNSString’s stringByDeletingLastPathComponent()’s stringByAppendingPathComponent:newName) –’s stringByAppendingPathExtension:anExtension
  
  
–Rename
  
if theNSFileManager’s fileExistsAtPath:newPath then
    return true
  else
    set theResult to theNSFileManager’s moveItemAtPath:POSIXPathNSString toPath:newPath |error|:(missing value)
    
if (theResult as integer = 1) then
      return (newPath as string)
    else
      return false
    end if
  end if
end renameFileItem

★Click Here to Open This Script 

Posted in file File path regexp Text | Tagged 10.14savvy 10.15savvy NSFileManager NSMutableArray NSPredicate NSRegularExpressionSearch NSString NSURL | Leave a comment

Safariで現在見えている表を抽出してCSV書き出しv3

Posted on 10月 8, 2019 by Takaaki Naganoya

Safariの最前面のウィンドウで表示中のページのうち、現在ウィンドウ内に表示中の表要素をCSV書き出ししてNumbersでオープンするAppleScriptの改良版です。HTMLのtable中にrowspan(複数セルを行方向に連結)とcolspan(複数セルを列方向に連結)の属性値が指定されていた場合に対応します。

–> Download table2CSV_visibleonly_v2 (Code-Signed AppleScript applet with Framework and Library in its bundle)

各DOM ElementsのWebコンテンツ中の表示座標を取得して、絞り込みを行なっています。ただし、各DOM座標はWebブラウザのスクロールにしたがって数値が変わる(相対座標)ため、少々手こずりました。また、本Scriptでは上下スクロールのみ考慮してDOM要素の抽出を行なっており、横に長いページの横方向スクロールは考慮しておりません。

このバージョンではrowspan / colspanへの対処を追加しました。

行単位で(1次元配列ベースで)表を作っていてはとても対処できなかったので、HTMLの表と同じセル数のヌル文字が入った2次元配列を作成し、そこにX座標/Y座標を指定してセルを埋めるように処理内容を変更しました。また、rowspan/colspanの属性を見つけた場合には、結合されていた複数セルを個別の(同じ値を入れた)セルに分解しています。

本バージョンでは、1つのセル(td)でrowspanとcolspanを同時に指定しないことが処理の前提条件となっています。また、一番上の行がヘッダーの場合を想定しており、一番左の列がヘッダーになっているケースには対処しておりません。

AppleScript名:Safariで現在見えている表を抽出してCSV書き出しv3.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/22
–  Modified on: 2019/10/07
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader
use aLib : script "arrayLib"

property NSUUID : a reference to current application’s NSUUID
property NSString : a reference to current application’s NSString
property HTMLDocument : a reference to current application’s HTMLDocument
property NSMutableArray : a reference to current application’s NSMutableArray
property NSJSONSerialization : a reference to current application’s NSJSONSerialization

set aTag to "table"

set indRes to getVisibleElementIndexList(aTag) of me
if indRes = false or indRes = {} then
  display notification "No Visible Table in Web browser"
  
return
end if

tell application "Safari"
  tell front document
    set aSource to source
  end tell
end tell

repeat with i in indRes
  set inList to filterATableAndPaseCells(aSource, i, aTag) of me
  
  
if inList = false or inList = {} then return
  
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text
  
set aNewFile to ((path to desktop) as string) & aUUID & ".csv"
  
saveAsCSV(inList, aNewFile) of me
  
  
tell application "Numbers"
    activate
    
open (aNewFile as alias)
  end tell
end repeat

on filterATableAndPaseCells(aSource as string, targInd as integer, aTag as string)
  set aHTML to current application’s HTMLDocument’s documentWithString:(aSource as string)
  
  
–Table要素をリストアップ
  
set eList to (aHTML’s nodesMatchingSelector:aTag) as list
  
set aObj to contents of item (targInd + 1) of eList
  
  
–Count columns of Table Header (Count only)
  
set aTableHeader to (aObj’s nodesMatchingSelector:"tr")’s firstObject()
  
set hList to aTableHeader’s nodesMatchingSelector:"th"
  
set hStrList to {}
  
repeat with i1 in hList
    set hCellStr to i1’s textContent() as string
    
set the end of hStrList to (hCellStr)
  end repeat
  
set hLen to length of hStrList –count columns
  
  
  
–Acquire whole table body contents
  
set aTableBody to (aObj’s nodesMatchingSelector:"tbody")’s firstObject()
  
set bList to (aTableBody’s nodesMatchingSelector:"tr") as list
  
  
set rCount to (length of bList) –count rows
  
  
–行単位ループ
  
set yCount to 1
  
set attrList to make2DBlankArray(hLen, rCount) of aLib
  
  
repeat with i2 in bList
    set bb2List to {}
    
set i3 to (i2’s nodesMatchingSelector:"th") as list
    
if i3 = {} then
      set i3 to (i2’s nodesMatchingSelector:"td") as list
    end if
    
    
–カラム単位ループ
    
set xCount to 1
    
repeat with i4 in i3
      set anAttr to i4’s attributes()
      
set colAtr to (anAttr’s valueForKey:"colspan")
      
set rowAttr to (anAttr’s valueForKey:"rowspan")
      
set cellStr to i4’s textContent() as string
      
      
if colAtr is not equal to missing value then
        –colspan処理
        
set colNum to colAtr as integer
        
set attrList to xFill(xCount, yCount, attrList, cellStr, colNum) of aLib
        
      else if rowAttr is not equal to missing value then
        –rowspan処理
        
set rowNum to rowAttr as integer
        
set attrList to yFill(xCount, yCount, attrList, cellStr, rowNum) of aLib
        
      else if cellStr is not equal to "" then
        –通常処理
        
repeat with ii from xCount to hLen
          set aRes to getItemByXY(ii, yCount, attrList, "") of aLib
          
if aRes = "" then
            set attrList to setItemByXY(ii, yCount, attrList, cellStr) of aLib
            
exit repeat
          else
            set xCount to xCount + 1
          end if
        end repeat
        
      end if
      
      
set xCount to xCount + 1
    end repeat
    
    
set yCount to yCount + 1
  end repeat
  
  
return attrList
end filterATableAndPaseCells

–Safariのウィンドウ上で表示中のDOM Elementsを座標計算して返す
on getVisibleElementIndexList(aTag as string)
  tell application "Safari"
    set dCount to count every document
    
if dCount = 0 then return false
    
    
set jRes to do JavaScript "var winWidth = window.innerWidth,
winHeight = window.innerHeight,
winLeft = window.scrollX,
winTop = window.scrollY,
winBottom = winTop + winHeight,
winRight = winLeft + winWidth,
    elementsArray = document.body.getElementsByTagName(’" & aTag & "’),
    elemLen = elementsArray.length,
inView = [];
      
    var step;
    for (step = 0 ; step < elemLen ; step++) {
      var tmpElem = document.body.getElementsByTagName(’" & aTag & "’)[step];
      var bVar = tmpElem.getBoundingClientRect();
      if (bVar.top > 0 && bVar.top < winHeight) {
        inView.push(step);
      }
    }
    JSON.stringify(inView);"
in front document
    
    
set jList to parseJSONAsList(jRes) of me
    
return jList
    
  end tell
end getVisibleElementIndexList

on parseJSONAsList(jsRes as string)
  set jsonString to NSString’s stringWithString:jsRes
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict as list
end parseJSONAsList

–Save 2D List to CSV file
on saveAsCSV(aList as list, aPath)
  set crlfChar to (string id 13) & (string id 10)
  
set LF to (string id 10)
  
set wholeText to ""
  
  
repeat with i in aList
    set newLine to {}
    
    
–Sanitize (Double Quote)
    
repeat with ii in i
      set jj to ii as text
      
set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote
      
set the end of newLine to kk
    end repeat
    
    
–Change Delimiter
    
set aLineText to ""
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to "\",\""
    
set aLineList to newLine as text
    
set AppleScript’s text item delimiters to curDelim
    
    
set aLineText to repChar(aLineList, return, "") of me –delete return
    
set aLineText to repChar(aLineText, LF, "") of me –delete lf
    
    
set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF
  end repeat
  
  
if (aPath as string) does not end with ".csv" then
    set bPath to aPath & ".csv" as Unicode text
  else
    set bPath to aPath as Unicode text
  end if
  
  
writeToFileAsUTF8(wholeText, bPath, false) of me
  
end saveAsCSV

on writeToFileAsUTF8(this_data, target_file, append_data)
  tell current application
    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 as «class utf8» 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 tell
end writeToFileAsUTF8

on repChar(origText as text, targChar as text, repChar as text)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

★Click Here to Open This Script 

Posted in file JavaScript JSON list Record Text | Tagged 10.12savvy 10.13savvy 10.14savvy HTMLDocument NSJSONSerialization NSMutableArray NSString NSUUID Numbers Safari | 4 Comments

Safariで現在見えている表を抽出してCSV書き出し

Posted on 9月 24, 2019 by Takaaki Naganoya

Safariの最前面のウィンドウで表示中のページのうち、現在ウィンドウ内に表示中の表要素をCSV書き出ししてNumbersでオープンするAppleScriptです。

このところ下調べを行なっていた「Webブラウザで表示中の要素を処理する」「表示中ではない要素は処理をしない」というScriptです。

これで、「表の一部を選択しておく」とかいった操作は不要になりました。ウィンドウ内に表示されている表をWebコンテンツ内部の表示座標をもとに自動抽出します。表示エリア外に位置しているものは書き出し処理しません。

各DOM ElementsのWebコンテンツ中の表示座標を取得して、絞り込みを行なっています。ただし、各DOM座標はWebブラウザのスクロールにしたがって数値が変わる(相対座標)ため、少々手こずりました。また、本Scriptでは上下スクロールのみ考慮してDOM要素の抽出を行なっており、横に長いページの横方向スクロールは考慮しておりません。

本Scriptは大量一括処理を志向するプログラムではなく、「見えているもの」をそのまま処理してほしいという考えで作ったものでもあり、Webブラウザ(Safari)で表示中のページのソースを取得してそのまま処理しています。つまり、ユーザーが閲覧中のページのデータそのものを処理しています。

これは、ページのソースを取得するコマンドを持っていないGoogle Chromeにはできない処理です(同じURLの内容を別途curlコマンドなどで取得すればOK。Cookie値などの再現が大変でしょうけれども)。

その他、実際に作って使ってみた感想は、装飾用に使われている表データまで取り込んでしまう点に不満があるぐらいでしょうか。これら「ゴミデータ」(再利用する価値のない装飾用の表データ)を区別するために、行数が足りない場合には書き出さないといった「足切り」を行う必要性を感じます。

–> Download VisibleTableExporter(Code-signed executable applet with Framework in its bundle)

AppleScript名:Safariで現在見えている表を抽出してCSV書き出し.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/22
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property NSUUID : a reference to current application’s NSUUID
property NSString : a reference to current application’s NSString
property HTMLDocument : a reference to current application’s HTMLDocument
property NSMutableArray : a reference to current application’s NSMutableArray
property NSJSONSerialization : a reference to current application’s NSJSONSerialization

set aTag to "table"

set indRes to getVisibleElementIndexList(aTag) of me
if indRes = false or indRes = {} then
  display notification "No Visible Table in Web browser"
  
return
end if

tell application "Safari"
  tell front document
    set aSource to source
  end tell
end tell

repeat with i in indRes
  set inList to filterATableAndPaseCells(aSource, i, aTag) of me
  
if inList = false or inList = {} then return
  
set aUUID to current application’s NSUUID’s UUID()’s UUIDString() as text
  
set aNewFile to ((path to desktop) as string) & aUUID & ".csv"
  
saveAsCSV(inList, aNewFile) of me
  
  
tell application "Numbers"
    open (aNewFile as alias)
  end tell
end repeat

tell application "Numbers" to activate

on filterATableAndPaseCells(aSource as string, targInd as integer, aTag as string)
  set aHTML to current application’s HTMLDocument’s documentWithString:(aSource as string)
  
  
–Table要素をリストアップ
  
set eList to (aHTML’s nodesMatchingSelector:aTag) as list
  
set aObj to contents of item (targInd + 1) of eList
  
  
  
–Count columns of Table Header
  
set aTableHeader to (aObj’s nodesMatchingSelector:"tr")’s firstObject()
  
set hList to aTableHeader’s nodesMatchingSelector:"th"
  
set hStrList to {}
  
repeat with i1 in hList
    set the end of hStrList to i1’s textContent() as string
  end repeat
  
set hLen to length of hStrList –count columns
  
  
–Acquire whole table body contents
  
set aTableBody to (aObj’s nodesMatchingSelector:"tbody")’s firstObject()
  
set bList to aTableBody’s nodesMatchingSelector:"td"
  
set bbList to {}
  
repeat with i2 in bList
    set the end of bbList to i2’s textContent() as string
  end repeat
  
  
set tbList to makeList1DTo2D(bbList, hLen) of me
  
  
return {hStrList} & tbList
end filterATableAndPaseCells

–1D Listを2D化
on makeList1DTo2D(orig1DList as list, aMax)
  set tbList to {}
  
set tmpList to {}
  
set aCount to 1
  
  
repeat with i3 in orig1DList
    set j to contents of i3
    
set the end of tmpList to j
    
    
if aCount ≥ aMax then
      set aCount to 1
      
set the end of tbList to tmpList
      
set tmpList to {}
    else
      set aCount to aCount + 1
    end if
  end repeat
  
  
return tbList
end makeList1DTo2D

–Safariのウィンドウ上で表示中のDOM Elementsを座標計算して返す
on getVisibleElementIndexList(aTag as string)
  tell application "Safari"
    set dCount to count every document
    
if dCount = 0 then return false
    
    
set jRes to do JavaScript "var winWidth = window.innerWidth,
winHeight = window.innerHeight,
winLeft = window.scrollX
winTop = window.scrollY,
winBottom = winTop + winHeight,
winRight = winLeft + winWidth,
    elementsArray = document.body.getElementsByTagName(’" & aTag & "’),
    elemLen = elementsArray.length,
inView = [];
      
    var step;
    for (step = 0 ; step < elemLen ; step++) {
      var tmpElem = document.body.getElementsByTagName(’" & aTag & "’)[step];
      var bVar = tmpElem.getBoundingClientRect();
      if (bVar.top > 0 && bVar.top < winHeight) {
        inView.push(step);
      }
    }
    JSON.stringify(inView);"
in front document
    
    
set jList to parseJSONAsList(jRes) of me
    
return jList
    
  end tell
end getVisibleElementIndexList

on parseJSONAsList(jsRes as string)
  set jsonString to NSString’s stringWithString:jsRes
  
set jsonData to jsonString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aJsonDict to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
  
return aJsonDict as list
end parseJSONAsList

–Save 2D List to CSV file
on saveAsCSV(aList as list, aPath)
  set crlfChar to (string id 13) & (string id 10)
  
set LF to (string id 10)
  
set wholeText to ""
  
  
repeat with i in aList
    set newLine to {}
    
    
–Sanitize (Double Quote)
    
repeat with ii in i
      set jj to ii as text
      
set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote
      
set the end of newLine to kk
    end repeat
    
    
–Change Delimiter
    
set aLineText to ""
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to "\",\""
    
set aLineList to newLine as text
    
set AppleScript’s text item delimiters to curDelim
    
    
set aLineText to repChar(aLineList, return, "") of me –delete return
    
set aLineText to repChar(aLineText, LF, "") of me –delete lf
    
    
set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF
  end repeat
  
  
if (aPath as string) does not end with ".csv" then
    set bPath to aPath & ".csv" as Unicode text
  else
    set bPath to aPath as Unicode text
  end if
  
  
writeToFileAsUTF8(wholeText, bPath, false) of me
  
end saveAsCSV

on writeToFileAsUTF8(this_data, target_file, append_data)
  tell current application
    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 as «class utf8» 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 tell
end writeToFileAsUTF8

on repChar(origText as text, targChar as text, repChar as text)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to targChar
  
set tmpList to text items of origText
  
set AppleScript’s text item delimiters to repChar
  
set retText to tmpList as string
  
set AppleScript’s text item delimiters to curDelim
  
return retText
end repChar

★Click Here to Open This Script 

Posted in file JavaScript JSON list Text | Tagged 10.12savvy 10.13savvy 10.14savvy HTMLDocument NSJSONSerialization NSMutableArray NSString NSUUID Numbers Safari | 1 Comment

指定アプリケーションの指定言語のstringsファイルの内容をすべて取り出す

Posted on 9月 21, 2019 by Takaaki Naganoya

バンドルIDで指定したアプリケーションのResourcesフォルダ中の、指定ロケールのstringsファイルをすべて読み込んでkeyを取り出すAppleScriptです。

アプリケーション内の指定のローカライズしたメッセージ(テキスト)の内容を取り出すことはできますが、そのためにはキーを知っておく必要があります。そのキーを取り出してローカライズされた文字列を取り出すため、キーを調査してみました。

これをそのまま何かのツールなりScriptに使うというものではなく、いわゆる「下調べ」のためのScriptの部品です。

ほぼ、ありもののルーチンを再利用しただけのプログラムでできていますが、ありものはありもので、数千本のAppleScriptのストックからそれを探し出してくるのも一苦労です。

アプリケーションバンドル内のResourcesフォルダ以下の(各lprojフォルダ以下の)stringsファイルの中身はまんま(シリアライズした)NSDictionaryなので、そのままDictionaryに読み込んでallkey()などのメソッドを実行できます。昔のstringsファイルはテキストファイルだったような気がしますが、テキストファイルだとparseしないといけないんで、ビルド時にparseしておいてDictionaryの状態でファイルに書いておくのは正しいと思います。

Xcode 8あたりから、アプリケーションのローカライズのルールが変更になったようで、基本となる言語環境がBase.lprojになり、英語はen.lprojといった1ローカライズ言語という位置付けに変更になりました。

NSLocalizedStringFromTableInBundleを使ってアクセスしようかとも思ったのですが、こちらはまだ目的を達成できていません。


▲Xcode 11上で実験的に作成したAppleScriptアプリケーションのバンドル構造(左)。ローカライズしていない状態だとEnglishではなくBase.lprojが作成される。右はKeynote.app/Contents/Resources/ja.lprojフォルダの内容。stringsファイルが大量に存在している

AppleScript名:指定アプリケーションの指定言語のstringsファイルの内容をすべて取り出す.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/21
—
–  Copyright © 2019 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

property |NSURL| : a reference to current application’s |NSURL|
property NSArray : a reference to current application’s NSArray
property NSBundle : a reference to current application’s NSBundle
property NSPredicate : a reference to current application’s NSPredicate
property NSDictionary : a reference to current application’s NSDictionary
property NSWorkspace : a reference to current application’s NSWorkspace
property NSFileManager : a reference to current application’s NSFileManager
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSURLTypeIdentifierKey : a reference to current application’s NSURLTypeIdentifierKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants

set targID to "com.apple.iWork.Keynote"

set bRes to getLocalizationsFromBundleID(targID) of me
–>  {"de", "he", "en_AU", "ar", "el", "ja", "en", "uk", "es_419", "zh_CN", "es", "da", "it", "sk", "pt_PT", "ms", "sv", "cs", "ko", "Base", "no", "hu", "zh_HK", "tr", "pl", "zh_TW", "en_GB", "vi", "ru", "fr_CA", "fr", "fi", "id", "nl", "th", "pt", "ro", "hr", "hi", "ca"}

set curLang to first item of (choose from list bRes)
set aPath to retPathFromBundleID(targID) of me

set aPath to aPath & "/Contents/Resources/" & curLang & ".lproj"
set sList to getFilepathListByUTI(aPath, "com.apple.xcode.strings-text", "POSIX") of me

set aMDict to NSMutableDictionary’s new()

repeat with i in sList
  set j to contents of i
  
set aDict to (NSDictionary’s alloc()’s initWithContentsOfFile:j)
  (
aMDict’s addEntriesFromDictionary:aDict)
end repeat

return aMDict’s allKeys() as list
–> {"Interactive Bubble Chart", "Rehearse Slideshow", "PMT_ARGUMENT_1", "ACCRINTM_ARGUMENT_4_MODE_0", "Truck_378",….}

on getLocalizationsFromBundleID(aBundleID)
  set aRes to retPathFromBundleID(aBundleID) of me
  
if aRes = false then error "Wrong Bundle ID."
  
return getSpecifiedAppFilesLocalizationListWithDuplication(aRes) of me
end getLocalizationsFromBundleID

–指定アプリケーションファイルの、指定Localeにおけるローカライズ言語リストを求める。重複を許容
on getSpecifiedAppFilesLocalizationListWithDuplication(appPOSIXpath)
  set aURL to (|NSURL|’s fileURLWithPath:appPOSIXpath)
  
set aBundle to NSBundle’s bundleWithURL:aURL
  
set locList to aBundle’s localizations()
  
return locList as list
end getSpecifiedAppFilesLocalizationListWithDuplication

on retPathFromBundleID(aBundleID)
  set aURL to NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
if aURL = missing value then return false –Error
  
return aURL’s |path|() as string
end retPathFromBundleID

on getFilepathListByUTI(aFolPOSIX, aUTI as string, aFileType as string)
  script spdFile
    property urlList : {}
  end script
  
  
if aFileType is not in {"file", "POSIX"} then return {}
  
  
set aFM to NSFileManager’s defaultManager()
  
set aFolExt to (aFM’s fileExistsAtPath:aFolPOSIX isDirectory:true) as boolean
  
if aFolExt = false then return {} –フォルダ自体が存在しなければヌルリストを返す
  
  
set aURL to |NSURL|’s fileURLWithPath:aFolPOSIX
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer)
  
set urlArray to (aFM’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value))
  
if urlArray = missing value then return {}
  
  
set (urlList of spdFile) to urlArray as list
  
set newList to {}
  
  
repeat with i in (urlList of spdFile)
    set j to POSIX path of i
    
set tmpUTI to my retUTIfromPath(j)
    
set utiRes to my filterUTIList({tmpUTI}, aUTI)
    
    
if utiRes is not equal to {} then
      if aFileType = "POSIX" then
        set the end of newList to j
      else if aFileType = "file" then
        set the end of newList to POSIX file j
      end if
    end if
    
  end repeat
  
  
return newList
end getFilepathListByUTI

–指定のPOSIX pathのファイルのUTIを求める
on retUTIfromPath(aPOSIXPath)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIXPath
  
set {theResult, theValue} to aURL’s getResourceValue:(reference) forKey:NSURLTypeIdentifierKey |error|:(missing value)
  
  
if theResult = true then
    return theValue as string
  else
    return theResult
  end if
end retUTIfromPath

–UTIリストが指定UTIに含まれているかどうか演算を行う
on filterUTIList(aUTIList, aUTIstr)
  set anArray to NSArray’s arrayWithArray:aUTIList
  
set aPred to NSPredicate’s predicateWithFormat_("SELF UTI-CONFORMS-TO %@", aUTIstr)
  
set bRes to (anArray’s filteredArrayUsingPredicate:aPred) as list
  
return bRes
end filterUTIList

★Click Here to Open This Script 

Posted in file Record URL UTI | Tagged 10.12savvy 10.13savvy 10.14savvy NSArray NSBundle NSDictionary NSDirectoryEnumerationSkipsHiddenFiles NSDirectoryEnumerationSkipsPackageDescendants NSFileManager NSMutableDictionary NSPredicate NSURL NSURLTypeIdentifierKey NSWorkspace | Leave a comment

tableExtractor

Posted on 9月 15, 2019 by Takaaki Naganoya

Safariで表示中のページのうち、テキストを選択中のキーワードを含む表をCSVファイルに書き出してNumbersでオープンするAppleScriptです。

–> Download tableExtractor Run-Only (Code-Signed Executable including Framework in its bundle)

–> Watch Demo movie

実行前にSafariの「開発」メニューから、「スマート検索フィールドからのJavaScriptの実行を許可」「AppleEventからのJavaScriptの実行を許可」を実行しておく必要があります(実行済みの場合には必要ありません)。


▲Safariで表示中のページのうち、CSV書き出ししたい表のテキストを選択


▲本Scriptで表をCSVに書き出してNumbersでオープン

以前に作成した「Safariで表示中のPageの選択中の文字を含む表データを取得」Scriptがいい線を行っていた(あらかじめ表中のテキストを選択しておく、という前提条件がかったるいかと思っていたのに、そうでもなかった)ので、ありもののサブルーチンを追加して、表部分のHTMLからのタグ削除やCSV書き出しなどを行えるようにしました。

本Scriptは表データをCSV書き出しする必要はどこにもないのですが、Numbers v6.1に「表を新規作成して表のセル数を指定すると多くの場合にエラーになる」というバグがあるので、Numbersを直接操作してデータ出力させることはやっていません。

処理時間もさほどかからないので、表示中のページのすべての表オブジェクトをCSV化したり、表を選択するUIを実装して、「どの表を出力するか?」という選択処理をしてもいいかもしれません。


▲漫然とMacOS日本語で書き出ししたため文字化けしたもの(左)、UTF8を指定して書き出ししたために文字化けしなくなったもの(右)

途中でCSV書き出しした表データに文字化けが発生していたのですが、これはUTF8でファイル書き出ししていなかったためでした。

本Scriptは前バージョンよりもキーワードの検出処理をていねいに行なっています。各TableのHTMLに対してタグの除去を行なったうえでWebブラウザ上で選択中の文字列を含んでいるかどうかをチェックしています。

AppleScript名:tableExtractor.scptd
— Created 2019-09-15 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

property NSString : a reference to current application’s NSString
property HTMLDocument : a reference to current application’s HTMLDocument
property NSMutableArray : a reference to current application’s NSMutableArray

tell application "Safari"
  set dList to every document –Sometimes "count every document"causes error
  
if length of dList = 0 then return
  
  
–Get URL
  
tell front document
    set aURL to URL
  end tell
  
  
–Get Selected Text
  
set aRes to do JavaScript "var selObj = window.getSelection();
  var selStr = (selObj).getRangeAt(0);
  unescape(selStr);"
in front document
  
  
if aRes = "" then return
end tell

set aRes to filterATableAndPaseCells(aURL, aRes) of me
if aRes = false then
  display notification "I could not filter table data…"
  
return
end if

–Save 2D List to temp CSV file on desktop folder
set savePath to ((path to desktop) as string) & (do shell script "uuidgen") & ".csv"
saveAsCSV(aRes, savePath) of me

tell application "Numbers"
  activate
  
open file savePath
end tell

on filterATableAndPaseCells(aURL, aKeyword)
  set aData to (do shell script "curl " & aURL)
  
set aHTML to current application’s HTMLDocument’s documentWithString:(aData as string)
  
  
–Table要素をリストアップ
  
set eList to (aHTML’s nodesMatchingSelector:"table")
  
  
–Table要素のうちSafari上で選択中の文字列を含むものをサーチ(指定データを含むものを抽出)
  
set hitF to false
  
repeat with i in eList
    set cellList to i’s children()’s array()
    
set htmlSource to i’s serializedFragment() as string –HTML source
    
set html2 to trimStrFromTo(htmlSource, "<", ">") of me
    
set html3 to repChar(html2, return, "") of me
    
    
if html3 contains aKeyword then
      set hitF to true
      
exit repeat
    end if
  end repeat
  
if hitF = false then return false
  
  
–Count columns of Table Header
  
set aTableHeader to (i’s nodesMatchingSelector:"tr")’s firstObject()
  
set hList to aTableHeader’s nodesMatchingSelector:"th"
  
set hStrList to {}
  
repeat with i1 in hList
    set the end of hStrList to i1’s textContent() as string
  end repeat
  
set hLen to length of hStrList –count columns
  
  
–Acquire whole table body contents
  
set aTableBody to (i’s nodesMatchingSelector:"tbody")’s firstObject()
  
set bList to aTableBody’s nodesMatchingSelector:"td"
  
set bbList to {}
  
repeat with i2 in bList
    set the end of bbList to i2’s textContent() as string
  end repeat
  
  
set tbList to makeList1DTo2D(bbList, hLen) of me
  
  
return {hStrList} & tbList
end filterATableAndPaseCells

–1D Listを2D化
on makeList1DTo2D(orig1DList, aMax)
  set tbList to {}
  
set tmpList to {}
  
set aCount to 1
  
  
repeat with i3 in orig1DList
    set j to contents of i3
    
set the end of tmpList to j
    
    
if aCount ≥ aMax then
      set aCount to 1
      
set the end of tbList to tmpList
      
set tmpList to {}
    else
      set aCount to aCount + 1
    end if
  end repeat
  
  
return tbList
end makeList1DTo2D

on trimStrFromTo(aParamStr, fromStr, toStr)
  set theScanner to current application’s NSScanner’s scannerWithString:aParamStr
  
set anArray to current application’s NSMutableArray’s array()
  
  
repeat until (theScanner’s isAtEnd as boolean)
    set {theResult, theKey} to theScanner’s scanUpToString:fromStr intoString:(reference)
    
    
theScanner’s scanString:fromStr intoString:(missing value)
    
set {theResult, theValue} to theScanner’s scanUpToString:toStr intoString:(reference)
    
if theValue is missing value then set theValue to ""
    
    
theScanner’s scanString:toStr intoString:(missing value)
    
    
anArray’s addObject:theValue
  end repeat
  
  
if anArray’s |count|() = 0 then return aParamStr
  
  
copy aParamStr to curStr
  
repeat with i in (anArray as list)
    set curStr to repChar(curStr, fromStr & i & toStr, "") of me
  end repeat
  
  
return curStr
end trimStrFromTo

on repChar(aStr, targStr, repStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:targStr withString:repStr
  
set cString to bString as string
  
return cString
end repChar

–2D List to CSV file
on saveAsCSV(aList, aPath)
  –set crlfChar to (ASCII character 13) & (ASCII character 10)
  
set crlfChar to (string id 13) & (string id 10)
  
set LF to (string id 10)
  
set wholeText to ""
  
  
repeat with i in aList
    set newLine to {}
    
    
–Sanitize (Double Quote)
    
repeat with ii in i
      set jj to ii as text
      
set kk to repChar(jj, string id 34, (string id 34) & (string id 34)) of me –Escape Double Quote
      
set the end of newLine to kk
    end repeat
    
    
–Change Delimiter
    
set aLineText to ""
    
set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to "\",\""
    
set aLineList to newLine as text
    
set AppleScript’s text item delimiters to curDelim
    
    
set aLineText to repChar(aLineList, return, "") of me –delete return
    
set aLineText to repChar(aLineText, LF, "") of me –delete lf
    
    
set wholeText to wholeText & "\"" & aLineText & "\"" & crlfChar –line terminator: CR+LF
  end repeat
  
  
if (aPath as string) does not end with ".csv" then
    set bPath to aPath & ".csv" as Unicode text
  else
    set bPath to aPath as Unicode text
  end if
  
  
writeToFileUTF8(wholeText, bPath, false) of me
  
end saveAsCSV

on writeToFileUTF8(this_data, target_file, append_data)
  tell current application
    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 as «class utf8» 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 tell
end writeToFileUTF8

★Click Here to Open This Script 

Posted in file Internet JavaScript list Text | Tagged 10.12savvy 10.13savvy 10.14savvy HTMLDocument NSMutableArray NSString Numbers Safari | 1 Comment

sdefの内容を強引に取得する

Posted on 9月 12, 2019 by Takaaki Naganoya

指定アプリケーションのsdefの内容を強引に取得するAppleScriptです。

スクリプトエディタをAppleScriptからコントロールして、指定アプリケーションのsdef(AppleScript用語辞書)の内容を取得します。もちろん、AppleScript対応のアプリケーションに限ります。対応/非対応の判定はあらかじめ行なっておいてください。

そもそも、なんでこれが必要になったかといえば、各種AppleScript Libraryの整備のための資料として、既存のAppleScript対応アプリケーションの識別コード(4文字コード)を取得して確認しておきたかったためです。

そこで、まっとうな方法だと、

step 1:対象アプリケーションのInfo.plistの内容を確認してsdefの名称を取得

step 2:アプリケーションバンドル内のsdefを取得

という流れになります。これで済めば処理は非常に短時間に完了します。ファイル処理だけなので。

しかし、実際にやってみると…………標準的な方法でsdefを取得できないアプリケーションがいくつか存在することに気づきます。

(1)Adobe Illustratorなど

バンドル内にsdefが存在しない。動的にプログラムでsdefを生成しているのでは?

(2)scriptSuite+scriptTerminologyに分かれている場合

バンドル内にsdefではなく.scriptSuiteファイルと.scriptTerminologyファイルを格納。テキストエディット(TextEdit.app)やスクリプトエディタ(Script Editor.app)などが該当する。

また、一部のアプリケーション(MS-Officeなど)ではsdefファイルのうち一部を外部からincludeしているため、アプリケーションバンドル内のsdefファイルを読んだだけでは完全なsdefが取得できないといった問題もあります。

そこで、この「スクリプトエディタをコントロールしてsdefの内容を取得する」という頭の悪そうな処理が必要になりました。シェルのsdefコマンドを使えばいいんじゃない? という意見も出てきそうですが、現行環境(macOS 10.14.x)でsdefコマンドはうまく動いていないように見えます。

AppleScript名:sdefの内容を強引に取得する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2019/09/12
—
–  Copyright © 2019 jp.piyomarusoft, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aPath to (choose file of type "com.apple.application-bundle" default location (path to applications folder) with prompt "Select an application")
set sdefContents to getForceSdefContentsUsingSE2(aPath) of me

on getForceSdefContentsUsingSE2(anAlias)
  try
    tell application id "com.apple.ScriptEditor2"
      open anAlias
      
tell front document
        set dictPath to path
      end tell
    end tell
  on error erM
    return erM
  end try
  
  
  
tell current application
    set aSdef to read dictPath
  end tell
  
  
tell application id "com.apple.ScriptEditor2"
    tell front document
      close without saving
    end tell
  end tell
  
  
return aSdef
end getForceSdefContentsUsingSE2

★Click Here to Open This Script 

Posted in file sdef | Tagged 10.12savvy 10.13savvy 10.14savvy Script Editor | Leave a comment

指定フォルダ以下のアプリケーションを取得して、Scriptabilityをチェック

Posted on 9月 11, 2019 by Takaaki Naganoya

指定フォルダ以下のアプリケーションをSpotlightで検索して、それらのうちAppleScript対応の(Scriptableな)ものを集計するAppleScriptです。

Shane StanleyのMetadata Libを呼び出してSpotlight検索を行い、アプリケーションファイルの一覧を取得。その後、各アプリケーションのInfo.plistの情報を取得してNSAppleScriptEnabled=trueのアプリケーションのパスをlist(array)に追加します。

ただし、一部のApple製のアプリケーションに見られるように、NSAppleScriptEnabled=trueであるにもかかわらず、実際のAppleScriptサポート機能が実装されていないものもありますので(例:iBooks Author)これだけではScript対応かどうかの完全な判断は行えません。

一方、AppleScriptなどのOSA言語によるコントロールに対応していないアプリケーションはこれで完全にフィルタできます。NSAppleScriptEnabledのエントリがInfo.plistに存在しないものは非対応アプリケーションです。

非対応のアプリケーションに対して、AppleScriptからは起動、終了、ファイルのオープン、印刷などの基礎的な操作やアプリケーションの情報取得(名称やバージョンなど)、Finder経由で指定書類をアプリケーションでオープン、ぐらいの操作。また、GUI Scriptingによるメニューやボタンの強制操作しかできません。

AppleScript名:指定フォルダ以下のアプリケーションを取得して、Scriptabilityをチェック.scptd
— Created 2019-09-11 by Takaaki Naganoya
— 2019 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use mdLib : script "Metadata Lib" version "2.0.0" –https://macosxautomation.com/applescript/apps/Script_Libs.html

set origPath to POSIX path of (choose folder default location (path to applications folder))
set aResList to mdLib’s searchFolders:{origPath} searchString:("kMDItemContentType == %@") searchArgs:{"com.apple.application-bundle"}

set allCount to length of aResList
set sList to {}

repeat with i in aResList
  set j to contents of i
  
set sRes to retAppScriptabilityFromBundleIPath(j) of me
  
if sRes = true then
    set the end of sList to j
  end if
end repeat

return (length of sList)
–> 297

–指定パスからアプリケーションのScriptabilityをbooleanで返す
on retAppScriptabilityFromBundleIPath(appPath as string)
  set aDict to (current application’s NSBundle’s bundleWithPath:appPath)’s infoDictionary()
  
set aRes to aDict’s valueForKey:"NSAppleScriptEnabled"
  
if aRes = missing value then return false
  
return aRes as boolean
end retAppScriptabilityFromBundleIPath

★Click Here to Open This Script 

Posted in file list Spotlight | Tagged 10.12savvy 10.13savvy 10.14savvy NSBundle | Leave a comment

MailCore2でメールのeml形式ファイルを読み込んでリンクURL抽出

Posted on 8月 11, 2019 by Takaaki Naganoya

Mail.appからメールを書き出ししたeml形式のファイルをMailCore2 Frameworkで読み込んで、本文(HTML)中に記載されているリンクURLを抽出するAppleScriptです。

eml形式ファイルは、Mail.appからドラッグ&ドロップでメールを書き出したファイルです。書き出し後のファイルはMail.appの管轄ではなく、メールデータはFinderの専門外。AppleScriptではこのeml形式ファイルへのアクセスは一切できず、eml形式ファイルが相手では手も足も出ませんでした。

macOS 10.10でAppleScriptの主要ランタイムでCocoaの機能が利用できるようになり、これまでにアクセスできなかったデータ形式にアクセスできるようになりました。その典型例がこのemlファイルです。

メールの各種データへのアクセスを行えるMailCore2.frameworkを使えば、簡単にこのemlファイル内の各種情報にアクセスできます。

各種Frameworkに甘やかされまくって、HTMLを自力でparseするとかいったことは一切努力しようとしていない今日このごろ。MailCore2でメール本文をいいようにparseし、HTMLReader Frameworkで解釈したHTMLからリンクをひとまかせで抽出しています。

–> Download MailCore.framework
–> Download HTMLReader.framework

–> Downdload emlLinkExtracter (Code-Signed AppleScript Applet with Frameworks within its bundle)

AppleScript名:MailCore2でメールのeml形式ファイルを読み込んでリンクURL抽出
— Created 2017-01-18 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "MailCore" –https://github.com/MailCore/mailcore2
use framework "HTMLReader" –https://github.com/nolanw/HTMLReader

–emlファイルを選択して読み込み
set aPath to POSIX path of (choose file of type {"com.apple.mail.email"})
set fileContents to current application’s NSData’s dataWithContentsOfFile:aPath options:(current application’s NSDataReadingMappedIfSafe) |error|:(missing value)
if fileContents = missing value then return

–メール本文をHTML化
set aParser to current application’s MCOMessageParser’s messageParserWithData:fileContents
set aBody1 to current application’s NSString’s stringWithString:(aParser’s htmlBodyRendering())
set aData to aBody1’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
set aHTML to current application’s HTMLDocument’s documentWithData:aData contentTypeHeader:"text/html"

–HTMLからリンクURLを抽出
set aTextArray to ((aHTML’s nodesMatchingSelector:"a")’s textContent) as list –リンク文字
set aLinkArray to ((aHTML’s nodesMatchingSelector:"a")’s attributes’s valueForKeyPath:"href") as list –URL
–> {"http://peatix.com", … "https://peatix.com/contact"}

★Click Here to Open This Script 

Posted in file | Tagged 10.12savvy 10.13savvy 10.14savvy | Leave a comment

Post navigation

  • Older posts
  • Newer posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • CotEditorで2つの書類の行単位での差分検出
  • macOS 13.6.5 AS系のバグ、一切直らず
  • macOS 15, Sequoia
  • 初心者がつまづきやすい「log」コマンド
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Adobe AcrobatをAppleScriptから操作してPDF圧縮
  • メキシカンハットの描画
  • 与えられた文字列の1D Listのすべての順列組み合わせパターン文字列を返す v3(ベンチマーク用)
  • 2023年に書いた価値あるAppleScript
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • Numbersで選択範囲のセルの前後の空白を削除
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • AppleScriptによる並列処理
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • AppleScript入門③AppleScriptを使った「自動化」とは?

Tags

10.11savvy (1102) 10.12savvy (1243) 10.13savvy (1392) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (188) 14.0savvy (138) 15.0savvy (116) CotEditor (64) Finder (51) iTunes (19) Keynote (115) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (75) Pages (54) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC