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

タグ: NSURL

AS書類を書式で分解して再構成

Posted on 1月 12 by Takaaki Naganoya

AppleScriptの書類を読み込んでNSAttributedStringに変換し、書式情報にもとづいてNSDictionary in NSArrayに変換。この状態でデータ抽出などを行なっていたのですが、このデータをもとにNSAttributedStringに再構成するAppleScriptです。

本Scriptの通しの処理内容としては、読み込んだAppleScriptを書式情報をもとに分解して、再構成するという……大豆から豆腐を作って、豆腐を崩して豆を得る、みたいな内容です。

NSAttributedStringを再構成する部分の処理を書いていなかったので、必要に迫られて書いてみました。

AppleScript構文色分け設定情報をもとに処理するため、色分け設定で複数の構文要素で同じ色を指定している場合にはエラーを返します。単にNSAttributedStringを解釈して再構成する、といった処理であればAppleScript構文色分け設定へのアクセス自体は必要ないところですが、すでに不可分なレベルで組み込まれているため、現状ではそのままです。

いっそ、こうした処理をする前にplistをバックアップし、必要な情報を書き込んだplistを設定ファイルとして使用するといった処理を行った方がいいのかもしれません。これまでは、「そんな処理はとても行えない」と判断していましたが、よくよく考えればとくに苦労せずに実現できそうです。

AppleScript名:AS書類を書式で分解して再構成.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/01/12
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

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

property NSFont : a reference to current application’s NSFont
property NSColor : a reference to current application’s NSColor
property |NSURL| : a reference to current application’s |NSURL|
property NSString : a reference to current application’s NSString
property OSAScript : a reference to current application’s OSAScript
property NSDictionary : a reference to current application’s NSDictionary
property NSUnarchiver : a reference to current application’s NSUnarchiver
property NSMutableDictionary : a reference to current application’s NSMutableDictionary
property NSFontAttributeName : a reference to current application’s NSFontAttributeName
property NSMutableAttributedString : a reference to current application’s NSMutableAttributedString
property NSForegroundColorAttributeName : a reference to current application’s NSForegroundColorAttributeName

property asCol : {}

set asCol to getAppleScriptSourceColors() of me –AppleScript構文色分け設定をplistから読み込む

set aFile to POSIX path of (choose file of type {"com.apple.applescript.script", "com.apple.applescript.script-bundle"})
set aURL to |NSURL|’s fileURLWithPath:(aFile)
set theScript to current application’s OSAScript’s alloc()’s initWithContentsOfURL:aURL |error|:(missing value)
set styledText to theScript’s richTextSource()

–Styled TextをDictionarry in Listに
set sRes to getAttributeRunsFromAttrStringRBO(styledText) of styleTextToDict

–Dictionarry in ListをStyled Textに再構成
set bRes to retAttributedStringFromAttrDict(sRes) of attrDictToAttrStr

script styleTextToDict
  use AppleScript
  
use framework "Foundation"
  
use framework "AppKit"
  
use scripting additions
  
property parent : AppleScript
  
  
–Styled Textを
  
on getAttributeRunsFromAttrStringRBO(theStyledText)
    script aSpd
      property styleList : {}
    end script
    
    
set (styleList of aSpd) to {} —for output
    
    
set thePureString to theStyledText’s |string|() –pure string from theStyledText
    
    
set theLength to theStyledText’s |length|()
    
set startIndex to 0
    
set aCount to 0
    
    
repeat until (startIndex = theLength)
      set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex}
      
      
–String  
      
set aText to (thePureString’s substringWithRange:theRange) as string
      
set strLen to (length of aText)
      
      
–Color
      
set aColor to (theAtts’s valueForKeyPath:"NSColor")
      
if aColor is not equal to missing value then
        set aSpace to aColor’s colorSpace()
        
        
set aRed to (aColor’s redComponent()) * 255
        
set aGreen to (aColor’s greenComponent()) * 255
        
set aBlue to (aColor’s blueComponent()) * 255
        
        
set colList to {aRed as integer, aGreen as integer, aBlue as integer} –for comparison
        
set colStrForFind to (aRed as integer as string) & " " & (aGreen as integer as string) & " " & (aBlue as integer as string) –for filtering
      else
        set colList to {0, 0, 0}
        
set colStrForFind to "0 0 0"
      end if
      
      
–Font
      
set aFont to (theAtts’s valueForKeyPath:"NSFont")
      
if aFont is not equal to missing value then
        set aDFontName to aFont’s displayName()
        
set aDFontSize to aFont’s pointSize()
      end if
      
      
–Range
      
set the end of (styleList of aSpd) to {stringVal:aText, atrIndex:aCount, colorStr:colStrForFind, colorVal:colList, fontName:aDFontName as string, fontSize:aDFontSize}
      
set startIndex to current application’s NSMaxRange(theRange)
      
      
set aCount to aCount + 1
    end repeat
    
    
return (styleList of aSpd)
    
  end getAttributeRunsFromAttrStringRBO
  
  
–NSColorからRGBの値を取り出す
  
on retColListFromNSColor(aCol, aMAX as integer)
    using terms from scripting additions
      set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school
      
set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school
      
set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school
    end using terms from
    
if aRed > aMAX then set aRed to aMAX
    
if aGreen > aMAX then set aGreen to aMAX
    
if aBlue > aMAX then set aBlue to aMAX
    
    
return {aRed, aGreen, aBlue}
  end retColListFromNSColor
  
  
–aMaxValを最大値とする数値でNSColorを作成して返す
  
on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
    set aRedCocoa to (redValue / aMaxVal) as real
    
set aGreenCocoa to (greenValue / aMaxVal) as real
    
set aBlueCocoa to (blueValue / aMaxVal) as real
    
set aAlphaCocoa to (alphaValue / aMaxVal) as real
    
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
    
return aColor
  end makeNSColorFromRGBAval
  
end script

script attrDictToAttrStr
  use AppleScript
  
use framework "Foundation"
  
use framework "AppKit"
  
use scripting additions
  
property parent : AppleScript
  
  
–書式つきテキストを組み立てる(メイン側)
  
on retAttributedStringFromAttrDict(attrRes)
    script aSpd
      property styleList : {}
    end script
    
    
set (styleList of aSpd) to {} —for output
    
    
set allAttrStr to NSMutableAttributedString’s alloc()’s init()
    
    
repeat with i in attrRes
      set j to contents of i
      
set tmpAttrStr to makeRTFfromParameters(stringVal of j, fontName of j, fontSize of j, colorVal of j) of me
      
      (
allAttrStr’s appendAttributedString:tmpAttrStr)
    end repeat
    
    
return allAttrStr
  end retAttributedStringFromAttrDict
  
  
–書式つきテキストを組み立てる(パーツ組み立て用サブ側)
  
on makeRTFfromParameters(aStr as string, fontName as string, aFontSize as real, aColorList as list)
    –Font & Size
    
set aVal1 to NSFont’s fontWithName:fontName |size|:aFontSize
    
set aKey1 to (NSFontAttributeName)
    
    
–Color
    
copy aColorList to {rCol, gCol, bCOl}
    
set aVal2 to makeNSColorFromRGBAval(rCol, gCol, bCOl, 255, 255) of asCol
    
set aKey2 to (NSForegroundColorAttributeName)
    
    
set keyList to {aKey1, aKey2}
    
set valList to {aVal1, aVal2}
    
set attrsDictionary to NSMutableDictionary’s dictionaryWithObjects:valList forKeys:keyList
    
    
–Text
    
set attrStr to NSMutableAttributedString’s alloc()’s initWithString:aStr attributes:attrsDictionary
    
return attrStr
  end makeRTFfromParameters
  
  
  
–aMaxValを最大値とする数値でNSColorを作成して返す
  
on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
    set aRedCocoa to (redValue / aMaxVal) as real
    
set aGreenCocoa to (greenValue / aMaxVal) as real
    
set aBlueCocoa to (blueValue / aMaxVal) as real
    
set aAlphaCocoa to (alphaValue / aMaxVal) as real
    
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
    
return aColor
  end makeNSColorFromRGBAval
  
end script

–AppleScriptの構文色分けのカラー値をRGBで取得する
on getAppleScriptSourceColors()
  — get the plist info as a dictionary
  
set thePath to NSString’s stringWithString:"~/Library/Preferences/com.apple.applescript.plist"
  
set thePath to thePath’s stringByExpandingTildeInPath()
  
set theInfo to NSDictionary’s dictionaryWithContentsOfFile:thePath
  
  
— extract relevant part and loop through
  
set theArray to (theInfo’s valueForKey:"AppleScriptSourceAttributes") as list
  
  
set colList to {}
  
  
repeat with i from 1 to count of theArray
    set anEntry to item i of theArray
    
    
set colorData to NSColor of anEntry
    
set theColor to (NSUnarchiver’s unarchiveObjectWithData:colorData)
    
    
set {rVal, gVal, bVal} to retColListFromNSColor(theColor, 255) of me
    
    
set fontData to NSFont of anEntry
    
set theFont to (NSUnarchiver’s unarchiveObjectWithData:fontData)
    
    
set aFontName to theFont’s displayName() as text
    
set aFontSize to theFont’s pointSize()
    
    
set aColRec to {redValue:rVal, greenValue:gVal, blueValue:bVal, fontName:aFontName, fontSize:aFontSize}
    
    
set the end of colList to aColRec
  end repeat
  
  
return colList
end getAppleScriptSourceColors

–NSColorからRGBの値を取り出す
on retColListFromNSColor(aCol, aMAX as integer)
  using terms from scripting additions
    set aRed to round ((aCol’s redComponent()) * aMAX) rounding as taught in school
    
set aGreen to round ((aCol’s greenComponent()) * aMAX) rounding as taught in school
    
set aBlue to round ((aCol’s blueComponent()) * aMAX) rounding as taught in school
  end using terms from
  
  
if aRed > aMAX then set aRed to aMAX
  
if aGreen > aMAX then set aGreen to aMAX
  
if aBlue > aMAX then set aBlue to aMAX
  
  
return {aRed, aGreen, aBlue}
end retColListFromNSColor

★Click Here to Open This Script 

Posted in Color file Font OSA RTF | Tagged 13.0savvy 14.0savvy 15.0savvy NSAttributedString NSColor NSDictionary NSFont NSMutableAttributedString NSMutableDictionary NSString NSUnarchiver NSURL OSAScript | Leave a comment

指定のPDFの本文テキストの文字数をカウント

Posted on 3月 9, 2022 by Takaaki Naganoya

指定のPDFの本文テキストを取り出し、文字数をカウントするAppleScriptです。


▲「AppleScriptによるWebブラウザ自動操縦ガイド」の文字数をカウントしようとした

300ページを超える、わりと大きな本のPDFデータから文字を取り出したので、それなりの文字数になりました。つまり、Cocoaの有効活用を考えないとデータサイズ的につらそうなデータ量になることが予想されました。

当初、(文字処理については)Cocoaの機能をあまり活用しないv2を作ったものの、処理にM1 Mac miniで30秒ほどかかりました。すべてNSStringのまま(AppleScriptのstringに型変換せずに)処理してみたら、案の定、大幅に処理が高速化され6秒あまりで完了。

ただし、両者でカウントした文字数が1万文字ぐらい違っていたので、(PDFから取得した文字の)Unicode文字のNormalize方式を変更したところ、両者で同じ結果になりました。また、処理速度に改変前から大幅な変化はありませんでした。

文字数を数えるという処理だとさすがにデータ数が膨大になるため、NSStringで処理したほうがメリットが大きいようです。

# あれ? 5文字ぐらい違う、、、、絵文字の部分か?

AppleScript名:指定のPDFの本文テキストの文字数を数える v2
use AppleScript version "2.8" –macOS 12 or later
use framework "Foundation"
use framework "PDFKit" –Comment Out under macOS 11
–use framework "Quartz"–Uncomment under macOS 11
use scripting additions

script spd
  property cRes : ""
  
property cList : {}
end script

set (cRes of spd) to ""
set (cList of spd) to {}

set (cRes of spd) to getTextFromPDF(POSIX path of (choose file of type {"pdf"})) of me
set (cList of spd) to current application’s NSArray’s arrayWithArray:(characters of (cRes of spd))
set cLen to (cList of spd)’s |count|()
return cLen
–> 256137 (24.763sec)

on getTextFromPDF(posixPath)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
set thePDF to current application’s PDFDocument’s alloc()’s initWithURL:theURL
  
return (thePDF’s |string|()’s precomposedStringWithCompatibilityMapping()) as text
end getTextFromPDF

★Click Here to Open This Script 

AppleScript名:指定のPDFの本文テキストの文字数を数える v3
use AppleScript version "2.8" –macOS 12 or later
use framework "Foundation"
use framework "PDFKit" –Comment Out under macOS 11
–use framework "Quartz"–Uncomment under macOS 11
use scripting additions

script spd
  property cRes : ""
  
property cList : {}
end script

set (cRes of spd) to ""
set (cList of spd) to {}

set (cRes of spd) to getTextFromPDF(POSIX path of (choose file of type {"pdf"})) of me
set cLen to (cRes of spd)’s |length|
return cLen as anything
–> 256142 (6.28 sec)

on getTextFromPDF(posixPath)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
set thePDF to current application’s PDFDocument’s alloc()’s initWithURL:theURL
  
return (thePDF’s |string|()’s precomposedStringWithCompatibilityMapping())
end getTextFromPDF

★Click Here to Open This Script 

わずかとはいえ、違いが出ていることは確かなので、1つのPDFに対して2つの処理方法でテキストを取り出して、それを配列に入れて集合演算して差分をとってみました。M1でも処理に1分少々かかりました。こういう処理は、M1 MaxでもM1 Ultraでも所要時間は変わらないことでしょう(逆にM1の方が処理時間が短い可能性まである)。

どうやら改行コードの解釈で文字数カウント結果に違いが出たようです。

AppleScript名:テキストの抽出方法による文字数の相違チェック.scptd
use AppleScript version "2.8" –macOS 12 or later
use framework "Foundation"
use framework "PDFKit" –Comment Out under macOS 11
–use framework "Quartz"–Uncomment under macOS 11
use scripting additions

script spd
  property cRes : ""
  
property cList : {}
end script

set (cRes of spd) to ""
set (cList of spd) to {}

set docPath to POSIX path of (choose file of type {"pdf"})

set (cRes of spd) to getTextFromPDF1(docPath) of me
set (cList of spd) to characters of (cRes of spd)
set anArray to current application’s NSArray’s arrayWithArray:(cList of spd)

set bStr to getTextFromPDF2(docPath) of me
set bArray to current application’s NSMutableArray’s new()
set bLen to (bStr’s |length|) as anything
repeat with i from 0 to (bLen – 1)
  set tmpStr to (bStr’s substringWithRange:(current application’s NSMakeRange(i, 1)))
  (
bArray’s addObject:(tmpStr))
end repeat

set aRes to diffLists(anArray, bArray) of me
set bRes to diffLists(bArray, anArray) of me
return {aRes, bRes}

on getTextFromPDF1(posixPath)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
set thePDF to current application’s PDFDocument’s alloc()’s initWithURL:theURL
  
return (thePDF’s |string|()’s precomposedStringWithCompatibilityMapping()) as text
end getTextFromPDF1

on getTextFromPDF2(posixPath)
  set theURL to current application’s |NSURL|’s fileURLWithPath:posixPath
  
set thePDF to current application’s PDFDocument’s alloc()’s initWithURL:theURL
  
return (thePDF’s |string|()’s precomposedStringWithCompatibilityMapping())
end getTextFromPDF2

–1D List同士のdiffを計算する
on diffLists(aList, bList)
  set aSet to current application’s NSMutableSet’s setWithArray:aList
  
set bSet to current application’s NSMutableSet’s setWithArray:bList
  
set aRes to calcSetDifference(aSet, bSet) of me
  
return aRes
end diffLists

–2つのNSMutableSetの補集合を求める
on calcSetDifference(aSet, bSet)
  aSet’s minusSet:bSet –補集合
  
set aRes to aSet’s allObjects() as list
  
return aRes
end calcSetDifference

★Click Here to Open This Script 

Posted in PDF Text | Tagged 12.0savvy NSURL PDFDocument | Leave a comment

指定アプリケーションの各言語のローカライズ名称を取得して、言語名をローカライズしてNumbersに出力

Posted on 2月 4, 2022 by Takaaki Naganoya

指定アプリケーションが対応している各ローカライズ言語におけるアプリケーション名称を取得して、言語名をcurrent localeに合わせて変換しつつCSV出力してNumbersでオープンするAppleScriptです。

アプリケーションバンドルを調査して、ローカライズ対応している言語の一覧を取得するプログラムは組んでありました。そのローカライズを順次調べて、InfoPlist.stringsファイルを読み込み、CFBundleNameのエントリを調べています。

これで、その言語向けにローカライズされた「名称」を調べられます。あとは、言語コードを名称に変換する処理(これも、ありもの)を組み合わせて、2次元配列(2D list)にまとめあげ、CSV書き出しして(ありもの)、Numbersでオープンしただけのものです。

言語名称については、currentLocaleを利用しているため、日本語環境で実行すれば日本語表現で出力されますし、英語環境で実行すれば英語表現で出力されます。

実際に、書籍に掲載する資料用のデータを作成するときに使ってみました。

言語 Code 「地図」アプリのローカライズ名称
ドイツ語 de Karten
ヘブライ語 he מפות
英語(オーストラリア) en_AU Maps
アラビア語 ar الخرائط
ギリシャ語 el Χάρτες
日本語 ja マップ
英語 en Maps
ウクライナ語 uk Карти
スペイン語(ラテンアメリカ) es_419 Mapas
中国語(中国本土) zh_CN 地图
スペイン語 es Mapas
デンマーク語 da Kort
イタリア語 it Mappe
スロバキア語 sk Mapy
ポルトガル語(ポルトガル) pt_PT Mapas
マレー語 ms Peta
スウェーデン語 sv Kartor
チェコ語 cs Mapy
韓国語 ko 지도
広東語(中国本土) yue_CN 地图
ノルウェー語 no Kart
ハンガリー語 hu Térképek
中国語(香港) zh_HK 地圖
トルコ語 tr Harita
ポーランド語 pl Mapy
中国語(台湾) zh_TW 地圖
英語(イギリス) en_GB Maps
ベトナム語 vi Bản đồ
ロシア語 ru Карты
フランス語(カナダ) fr_CA Plans
フランス語 fr Plans
フィンランド語 fi Kartat
インドネシア語 id Peta
オランダ語 nl Kaarten
タイ語 th แผนที่
ポルトガル語 pt Mapas
ルーマニア語 ro Hărți
クロアチア語 hr Karte
ヒンディー語 hi नक़्शा
カタロニア語 ca Mapes
AppleScript名:指定アプリケーションの各言語のローカライズ名称を取得して、言語名をローカライズしてNumbersに出力.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/02/04
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

set targFile to "InfoPlist.strings"
set targKey to "CFBundleName"

set aLocale to (current application’s NSLocale’s currentLocale())

set aLoc to path to applications folder
set anApp to POSIX path of (choose file of type {"com.apple.application-bundle"} default location aLoc)

set aBundle to getBundleFromPath(anApp) of me
set aName to aBundle’s objectForInfoDictionaryKey:(current application’s kCFBundleNameKey)

set aLocList to getSpecifiedAppFilesLocalizationListWithDuplication(aBundle) of me

set hitList to {}

repeat with i in aLocList
  set j to contents of i
  
  
if j is not equal to "Base" then
    set allPath to anApp & "/Contents/Resources/" & j & ".lproj/" & targFile
    
set aDict to (current application’s NSDictionary’s alloc()’s initWithContentsOfFile:allPath)
    
    
if aDict is not equal to missing value then
      set aVal to (aDict’s valueForKeyPath:(targKey))
      
if aVal is not equal to missing value then
        set aLang to getLangNameWithLocale(j, aLocale) of me
        
set the end of hitList to {aLang, j, aVal as string}
      end if
    else
      log {"Error in ", j}
    end if
  end if
end repeat

–一時ファイルをCSV形式でデスクトップに書き出し
set aPath to (path to desktop as string) & (do shell script "uuidgen") & ".csv"
saveAsCSV(hitList, aPath) of me

–書き出したCSVファイルをNumbersでオープン
tell application "Numbers"
  open (aPath as alias)
end tell

–Application path –> Bundle
on getBundleFromPath(aPOSIXpath)
  set aURL to current application’s |NSURL|’s fileURLWithPath:aPOSIXpath
  
set aWorkspace to current application’s NSWorkspace’s sharedWorkspace()
  
set appURL to aWorkspace’s URLForApplicationToOpenURL:aURL
  
set aBundle to current application’s NSBundle’s bundleWithURL:appURL
  
return aBundle
end getBundleFromPath

–指定Bundleのローカライズ言語リストを求める。重複を許容
on getSpecifiedAppFilesLocalizationListWithDuplication(aBundle)
  set locList to aBundle’s localizations()
  
return locList as list
end getSpecifiedAppFilesLocalizationListWithDuplication

on getLangNameWithLocale(langCode, aLocale)
  set aLangName to (aLocale’s displayNameForKey:(current application’s NSLocaleIdentifier) value:langCode) as string
  
return aLangName
end getLangNameWithLocale

–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
  
  
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 Language Locale Record | Tagged 10.15savvy 11.0savvy 12.0savvy NSBundle NSDictionary NSLocale NSURL NSWorkspace Numbers | Leave a comment

不可視プロセスで表示したNSAlertを最前面に表示

Posted on 1月 28, 2022 by Takaaki Naganoya

コンテクストメニューからAppleScriptを実行するツール「Service Station」を連日こづき回していろいろ試しています。

その中で、Service StationではScript実行をGUIなしの補助プログラムからosascriptコマンドを経由してAppleScriptを実行しているために、AppleScript中でNSAlertによるダイアログ表示をおこなった際に、ダイアログが最前面に表示されないという問題が生じていました。

これを見たedama2さんから、強制的にNSAlertダイアログを最前面に表示させるコードを提供していただいたので、実際にテストに用いたAppleScriptに入れたものをご紹介します。


▲そのまま普通にNSAlertダイアログを表示させたところ。Service Stationによるコンテクストメニューから実行すると、最前面に表示されないという問題があった


▲edama2さんからもたらされたコードを追加したNSAlertダイアログ。Service Stationから実行しても、問題なく最前面に表示される

コードは2行、効き目はばっちり!

--Move to frontmost (By edama2)
current application's NSApplication's sharedApplication()'s setActivationPolicy:(current application's NSApplicationActivationPolicyRegular)
current application's NSApp's activateIgnoringOtherApps:(true)

副作用として、Dockにターミナルのアイコンが表示され、最前面のアプリケーションが「osascript」になってしまいますが、気にならないところでしょう。

Program Name Name of runtime Support AppleScript document format AS Format NSAlert dialog is displayed in frontmost Can use GUI Scripting functions Can call AppleScript Libraries? Can call AS Library including Frameworks? Can call Generic Cocoa Functions? Can use Cocoa system notification functions?
Service Station osascript Script/Text Script/Applet Script/Text Script No-> Yes Yes Yes No Yes No
Script Menu osascript Script/Scriptd/Applet Script/Scriptd Yes Yes Yes No Yes Yes
Shortcuts Events MacHelper File Embedded Script Text Yes Yes Yes No Yes No
Switch Control Assistive Control File Embedded Script Script (archived) Yes Yes No No Yes No
FastScript 3 FastScripts Script Runner Script/Scriptd/Applet Script/Scriptd Yes Yes Yes No Yes

あれ? edama2さんのおかげで、Service Stationの弱点が1つなくなりましたよ。Service Stationについては、

(1)「Kind–> Script」「AppleScript」でテキストで書いた「.applescript」しかピックアップできないとかいう気の狂ったようなRulesの仕様は要・修正

(2)実行Scriptにフラットな.scptとテキストの.applescriptしか実行できないのはダメ。バンドル形式の.scptdの実行サポートは必須

(3)大量のAppleScriptをコンテクストメニューに入れると使い勝手が落ちるので、Rulesでもう少しこまかく条件付けできるとよさそう。ただし必須ではない

といったところでしょうか。

AppleScript名:Webダイアログ表示(強制最前面).scpt
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

my serviceStationDidSelect("", "", "")

on serviceStationDidSelect(targetedURL, selectedItemURLs, menuKind)
  set aList to {{label:"TEST1", value:403}, {label:"TEST2", value:301}, {label:"TEST3", value:101}, {label:"TEST4", value:65}}
  
set bList to sortRecListByLabel(aList, {"value"}, {false}) of me –降順ソート
  
  
–https://www.amcharts.com/demos/pie-chart/
  
set myStr to retHTML() of me
  
set jsonStr to array2DToJSONArray(bList) of me as string
  
set aString to current application’s NSString’s stringWithFormat_(myStr, jsonStr) as string
  
set paramObj to {myMessage:"Simple Pie Chart", mySubMessage:"This is a AMCharts test", htmlStr:aString, viewSize:{1000, 550}}
  
  
my webD’s displayWebDialog(paramObj)
end serviceStationDidSelect

on array2DToJSONArray(aList)
  set anArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value)
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

–リストに入れたレコードを、指定の属性ラベルの値でソート
on sortRecListByLabel(aRecList as list, aLabelStr as list, ascendF as list)
  set aArray to current application’s NSArray’s arrayWithArray:aRecList
  
  
set aCount to length of aLabelStr
  
set sortDescArray to current application’s NSMutableArray’s new()
  
repeat with i from 1 to aCount
    set aLabel to (item i of aLabelStr)
    
set aKey to (item i of ascendF)
    
set sortDesc to (current application’s NSSortDescriptor’s alloc()’s initWithKey:aLabel ascending:aKey)
    (
sortDescArray’s addObject:sortDesc)
  end repeat
  
  
return (aArray’s sortedArrayUsingDescriptors:sortDescArray) as list
end sortRecListByLabel

script webD
  —
  
–  Created by: Takaaki Naganoya
  
–  Created on: 2020/06/20
  
—
  
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
  
—
  
use AppleScript
  
use framework "Foundation"
  
use framework "AppKit"
  
use framework "WebKit"
  
use scripting additions
  
property parent : AppleScript
  
  
property |NSURL| : a reference to current application’s |NSURL|
  
property NSAlert : a reference to current application’s NSAlert
  
property NSString : a reference to current application’s NSString
  
property NSButton : a reference to current application’s NSButton
  
property WKWebView : a reference to current application’s WKWebView
  
property WKUserScript : a reference to current application’s WKUserScript
  
property NSURLRequest : a reference to current application’s NSURLRequest
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
  
property WKUserContentController : a reference to current application’s WKUserContentController
  
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
  
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd
  
  
property returnCode : 0
  
  
  
on displayWebDialog(paramObj)
    my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true
  end displayWebDialog
  
  
on browseStrWebContents:paramObj
    set aMainMes to myMessage of paramObj as string
    
set aSubMes to mySubMessage of paramObj as string
    
set htmlString to (htmlStr of paramObj) as string
    
–set jsDelimList to (jsDelimiters of paramObj) as list
    
set webSize to (viewSize of paramObj) as list
    
    
copy webSize to {aWidth, aHeight}
    
    
–WebViewをつくる
    
set aConf to WKWebViewConfiguration’s alloc()’s init()
    
    
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) configuration:aConf
    
aWebView’s setNavigationDelegate:me
    
aWebView’s setUIDelegate:me
    
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
    
using terms from scripting additions
      set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
    end using terms from
    
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
    
    
–Move to frontmost (By edama2)
    
current application’s NSApplication’s sharedApplication()’s setActivationPolicy:(current application’s NSApplicationActivationPolicyRegular)
    
current application’s NSApp’s activateIgnoringOtherApps:(true)
    
    
— set up alert  
    
set theAlert to NSAlert’s alloc()’s init()
    
tell theAlert
      its setMessageText:aMainMes
      
its setInformativeText:aSubMes
      
its addButtonWithTitle:"OK"
      
–its addButtonWithTitle:"Cancel"
      
its setAccessoryView:aWebView
      
      
set myWindow to its |window|
    end tell
    
    
— show alert in modal loop
    
NSRunningApplication’s currentApplication()’s activateWithOptions:0
    
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
    
    
if (my returnCode as number) = 1001 then error number -128
    
    
##後始末
    
aWebView’s stopLoading()
    
set js to "window.open(’about:blank’,’_self’).close();"
    
aWebView’s evaluateJavaScript:js completionHandler:(missing value)
    
set aWebView to missing value
    
  end browseStrWebContents:
  
  
  
on doModal:aParam
    set (my returnCode) to (aParam’s runModal()) as number
  end doModal:
  
  
  
on viewDidLoad:aNotification
    return true
  end viewDidLoad:
  
  
  
on fetchJSSourceString(aURL)
    set jsURL to |NSURL|’s URLWithString:aURL
    
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
    
return jsSourceString
  end fetchJSSourceString
  
  
  
on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
    using terms from scripting additions
      set a1Offset to offset of s1Str in aStr
      
if a1Offset = 0 then return false
      
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
      
set a2Offset to offset of s2Str in bStr
      
if a2Offset = 0 then return false
      
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
      
return cStr as string
    end using terms from
  end pickUpFromToStr
  
  
  
–リストを任意のデリミタ付きでテキストに
  
on retArrowText(aList, aDelim)
    using terms from scripting additions
      set aText to ""
      
set curDelim to AppleScript’s text item delimiters
      
set AppleScript’s text item delimiters to aDelim
      
set aText to aList as text
      
set AppleScript’s text item delimiters to curDelim
      
return aText
    end using terms from
  end retArrowText
  
  
on array2DToJSONArray(aList)
    set anArray to current application’s NSMutableArray’s arrayWithArray:aList
    
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
    
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
    
return resString
  end array2DToJSONArray
  
  
  
on parseByDelim(aData, aDelim)
    using terms from scripting additions
      set curDelim to AppleScript’s text item delimiters
      
set AppleScript’s text item delimiters to aDelim
      
set dList to text items of aData
      
set AppleScript’s text item delimiters to curDelim
      
return dList
    end using terms from
  end parseByDelim
  
end script

on retHTML()
  return "<!doctype html>
<head>
<meta charset=\"utf-8\">
<!– Styles –>
<style>
body { background-color: #000000; color: #ffffff; }
#chartdiv {
width: 100%;
height: 500px;
}

</style>
</head>
<body>

<!– Resources –>
<script src=\"https://www.amcharts.com/lib/4/core.js\"></script>
<script src=\"https://www.amcharts.com/lib/4/charts.js\"></script>
<script src=\"https://www.amcharts.com/lib/4/themes/dark.js\"></script>
<script src=\"https://www.amcharts.com/lib/4/themes/animated.js\"></script>

<!– Chart code –>
<script>
am4core.ready(function() {

// Themes begin
am4core.useTheme(am4themes_dark);
am4core.useTheme(am4themes_animated);
// Themes end

// Create chart instance
var chart = am4core.create(\"chartdiv\", am4charts.PieChart);

// Add data
chart.data = %@;

// Add and configure Series
var pieSeries = chart.series.push(new am4charts.PieSeries());
pieSeries.dataFields.value = \"value\";
pieSeries.dataFields.category = \"label\";
pieSeries.slices.template.stroke = am4core.color(\"#fff\");
pieSeries.slices.template.strokeOpacity = 1;

// This creates initial animation
pieSeries.hiddenState.properties.opacity = 1;
pieSeries.hiddenState.properties.endAngle = -90;
pieSeries.hiddenState.properties.startAngle = -90;

chart.hiddenState.properties.radius = am4core.percent(0);

}); // end am4core.ready()
</script>

<!– HTML –>
<div id=\"chartdiv\"></div>
</body>
</html>"

end retHTML

★Click Here to Open This Script 

Posted in JSON Review | Tagged 12.0savvy NSAlert NSButton NSRunningApplication NSString NSURL NSURLRequest Service Station WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

Intel MacとApple Silicon Macの速度差〜画像処理

Posted on 1月 2, 2022 by Takaaki Naganoya

macOS 11から12に移行して、なぜかリリースが近づくにつれて細かいバグや巨大なバグが顕在化し、まだ手放しでおすすめできる状況にないのが心苦しいところですが、AppleScriptからのCocoa呼び出しについてはIntel Mac/Apple Silicon Macでも速度が向上。とくに、Apple Silicon Macでの速度向上が顕著です。

で、特定の処理(巨大なlistやrecord in list同士の検索)でM1 Mac miniがiMac Proの2.5倍ぐらい速いといったベンチマークは出しているわけですが、いかにもApple Silicon Macで処理が速そうな画像処理のベンチマークを実施してみました。

Intel Mac mini 2014が手元からなくなってしまったので、以前のメインマシンであったMacBook Pro Retina 2012(MacBookPro10,1)と比較してみました。

ベンチマーク内容は、指定の画像が空白かどうかをチェックするというものです。フルHD画像でも4K画像でも8K画像でも、1ピクセルでも白くない点があれば検出できるという処理内容。外部のGPUImage FrameworkやPhotoshopのヒストグラム処理を呼び出すよりもAppleScriptだけで処理した方が速いというものです(8K画像だとPhotoshopの方が速いかも)。


▲画像解像度の変化と処理時間の相関。グラフは数値が小さいほど高速。巨大な画像になると処理速度差が大きくなる傾向にある?

だいたい、MacBook Pro Retina 2012と比べて3〜4倍ぐらいM1 Mac miniの方が高速です。MacBook Pro Retina 2012は2017年のMacBook Pro 13インチといい勝負ぐらいの速度が出ており、古い割にはごく最近まで使えていました。

そこから3〜4倍高速ということで、M1 Mac miniはコストの割にはパワフルです。ファンレスのMacBook Air M1でも同程度の速度が出るはずです。

一方で、M1 Pro/M1 Max搭載のMacBook ProでM1機よりも大幅に高速なのかと言われると……このぐらいの静止画の処理程度だとごくわずかな差しかつかないはずです。下手をすると、速度差がないかもしれません。

→ Download blank_image_detection_benchmark.zip (including script libraries)

–> Download test Data


▲バーが短いほど高速。M1がM1 Maxの2倍高速という結果が出てしまった。M1 Max MBPは2014年のIntel Mac miniより少し速いだけのマシンという結果に。2018年のIntel Mac miniとの比較だとM1 Max MBPよりもIntel Mac miniのほうが速そう

M1、M1 Max、M1 Ultra(最上位機種)で処理時間を比較した結果。予想どおりM1がM1 MaxやM1 Ultraよりも2倍以上高速。もはや8K画像ぐらいは、大きなデータにならないというべきなのか。

AppleScript名:画像の空白判定 v4_bench_時間計測.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/01/01
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7"
use framework "Foundation"
use framework "AppKit"
use scripting additions
use mdLib : script "Metadata Lib" version "2.0.0"
use easyTable : script "display table by list"

set aFol to choose folder

set aResList to perform search in folders {aFol} predicate string "kMDItemContentTypeTree CONTAINS %@" search arguments {"public.image"}

set aList to {}
repeat with i in aResList
  set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
  
  
set iRes to checkImageIsWhite(i) of blankImageKit of me
  
  
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
  
set c1Dat to b1Dat – a1Dat
  
  
set aName to (current application’s NSString’s stringWithString:i)’s lastPathComponent() as string
  
  
if iRes = true then
    set jRes to "White"
  else
    set jRes to "Black"
  end if
  
  
–log {aName, jRes}
  
  
set the end of aList to {aName, jRes, c1Dat}
end repeat

set fLabels to {"File name", "Result", "Estimate(Seconds)"}
set aRes to (display table by list aList main message "Blank Image check" size {800, 600} labels fLabels)
return aList

script blankImageKit
  use AppleScript version "2.7" — High Sierra (10.13) or later
  
use framework "Foundation"
  
use framework "AppKit"
  
use scripting additions
  
property parent : AppleScript
  
  
property NSData : a reference to current application’s NSData
  
property |NSURL| : a reference to current application’s |NSURL|
  
property NSColor : a reference to current application’s NSColor
  
property NSImage : a reference to current application’s NSImage
  
property NSBezierPath : a reference to current application’s NSBezierPath
  
property NSBitmapImageRep : a reference to current application’s NSBitmapImageRep
  
  
  
–Compare Original Data and
  
on checkImageIsWhite(aFile)
    set aPOSIXpath to POSIX path of aFile
    
set aURL to |NSURL|’s fileURLWithPath:(aPOSIXpath)
    
    
set aNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL)
    
set bNSImage to NSImage’s alloc()’s initWithContentsOfURL:(aURL)
    
    
set fillColor1 to NSColor’s clearColor()
    
set blankNSImage1 to drawImageWithFilledColor(aNSImage, fillColor1) of me
    
    
set fillColor2 to makeNSColorFromRGBAval(65535, 65535, 65535, 65535, 65535) of me –white
    
set blankNSImage2 to drawImageWithFilledColor(bNSImage, fillColor2) of me
    
    
set aTiff to blankNSImage1’s TIFFRepresentation()
    
set bTiff to blankNSImage2’s TIFFRepresentation()
    
    
set chkWhite to (aTiff’s isEqualToData:bTiff) as boolean
    
    
return chkWhite
  end checkImageIsWhite
  
  
  
on getSizeOfImage(anNSImage)
    set aSize to anNSImage’s |size|()
    
set aClass to class of aSize
    
if aClass = record then
      copy aSize to theSize –To macOS 10.12.x
    else –macOS 10.13 or later
      set sizeX to (item 1 of item 2 of aSize)
      
set sizeY to (item 2 of item 2 of aSize)
      
set theSize to {width:sizeX, height:sizeY}
    end if
    
return theSize
  end getSizeOfImage
  
  
  
–指定サイズの画像を作成し、背景を指定色で塗る
  
on drawImageWithFilledColor(anImage, fillColor)
    set aSize to getSizeOfImage(anImage) of me
    
    
anImage’s lockFocus()
    
    
set theRect to {{x:0, y:0}, {width:(width of aSize), height:(height of aSize)}}
    
set theNSBezierPath to NSBezierPath’s bezierPath
    
theNSBezierPath’s appendBezierPathWithRect:theRect
    
fillColor’s |set|()
    
theNSBezierPath’s fill()
    
    
anImage’s unlockFocus()
    
    
return anImage
  end drawImageWithFilledColor
  
  
  
–aMaxValを最大値とする数値でNSColorを作成して返す
  
on makeNSColorFromRGBAval(redValue as integer, greenValue as integer, blueValue as integer, alphaValue as integer, aMaxVal as integer)
    set aRedCocoa to (redValue / aMaxVal) as real
    
set aGreenCocoa to (greenValue / aMaxVal) as real
    
set aBlueCocoa to (blueValue / aMaxVal) as real
    
set aAlphaCocoa to (alphaValue / aMaxVal) as real
    
set aColor to NSColor’s colorWithCalibratedRed:aRedCocoa green:aGreenCocoa blue:aBlueCocoa alpha:aAlphaCocoa
    
return aColor
  end makeNSColorFromRGBAval
  
end script

★Click Here to Open This Script 

Posted in Image | Tagged 10.14savvy 10.15savvy 11.0savvy 12.0savvy NSBezierPath NSBitmapImageRep NSColor NSData NSDate NSImage NSURL | Leave a comment

CoreML Modelからパラメータを取得する v2

Posted on 12月 30, 2020 by Takaaki Naganoya

CoreML Modelのファイル(.mlmodelc)から入力パラメータおよび出力データの情報を取得するAppleScriptです。

テストに使ったのはMS Classifier(299×299ピクセルの画像を入力)で、入力画像をGundamのモビルスーツとして扱い、連邦軍機体かジオン軍機体かを判定するCoreML Modelです。

機械学習モデルのパラメータ/モデル解析を行うツール「Netron」にはWebアプリ版もあるので、JavaScriptやPythonで解析できてしまうのかもしれません(Netronのソースを読んでみたら、そういう感じのファイルだけ入っていて驚かされました)。

AppleScript名:CoreML Modelからパラメータを取得する v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/12/28
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — 10.13 or later
use framework "Foundation"
use framework "CoreML"
use scripting additions

set aVer to system version of (system info) –Get OS Version

considering numeric strings
  if aVer < "10.15" then
    –macOS 10.14。10.13は未検証
    
set mlFile to POSIX path of (choose folder) –mlmodelcを指定。macOS 10.14のFinder上ではフォルダに見えてしまう
  else
    –macOS 10.15 or later
    
set mlFile to POSIX path of (choose file of type {"com.apple.coreml.mlmodelc"}) –mlmodelcを指定。macOS 10.15以降のFinder上ではファイルに見える
  end if
end considering

set mlURL to current application’s |NSURL|’s fileURLWithPath:mlFile
set aModel to current application’s MLModel’s modelWithContentsOfURL:mlURL |error|:(missing value)
if aModel = missing value then return –mlmodelファイルだとエラーになる

set aMLDesc to aModel’s modelDescription
set inputDesc to aMLDesc’s inputDescriptionsByName()

–Input Information
set kList to inputDesc’s allKeys() as list
–> {"image"}

set vList to inputDesc’s allValues() as list
–> {(MLFeatureDescription) image : Image (Color, 299 x 299)}

–Output Information
set outputDeck to aMLDesc’s outputDescriptionsByName()
set k2List to outputDeck’s allKeys() as list
–> {"classLabel", "classLabelProbs"}

set v2List to outputDeck’s allValues() as list
–> {(MLFeatureDescription) classLabel : String, (MLFeatureDescription) classLabelProbs : Dictionary (String → Double)}

set vResList to {}
repeat with v in v2List
  set the end of vResList to v’s |name|() as string
end repeat
return vResList
–> {"classLabel", "classLabelProbs"}

★Click Here to Open This Script 

Posted in Machine Learning | Tagged 10.14savvy 10.15savvy 11.0savvy MLFeatureDescription MLModel NSURL | Leave a comment

アプリケーションが扱えるデータ拡張子を取得

Posted on 10月 28, 2020 by Takaaki Naganoya

指定のアプリケーションが扱えるデータ拡張子を取得するAppleScriptです。

アプリケーションバンドル内のInfo.plistの、CFBundleDocumentTypesエントリの中のCFBundleTypeExtensionsを拾ってリストで返します。用途は、このアプリケーションに表示させるためのものです。

ただ、この標準的といいますか一般的な処理でこのデータを拾ってこれないアプリケーション(凝った処理をしているもよう)がかなりの数存在し、それらについては個別に対応処理を書かないとダメっぽいです。

だいたい、AppleのiWork Appsが全滅。BBEditも独自の記述をしているらしく、Pixelmator Proも少し凝った書き方をしているもよう(Pixelmator Proが「凝っていない」部分なんてないみたいですが)。個別にInfo.plistからエントリをたどって拡張子リストを作るしかなさそうです。

OS内、具体的に言えばNSApplicationとかNSBundleにそういうInfo.plistをあさってこなくても対応ドキュメントデータ型を渡してくれるようなサービスがありそうな気がとてもするのですが、なかなか見つかりそうにありません。

あまりに対応が大変だった場合には、機能を盛り込むことを断念するかもしれません。メタデータから拾ってこれたりすると楽でよいのですが……。


▲結局、できた模様

AppleScript名:アプリケーションが扱えるデータ拡張子を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/10/28
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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

set aPath to POSIX path of (choose file of type {"com.apple.application-bundle"})
set aURL to (current application’s |NSURL|’s fileURLWithPath:aPath)
set exResList to getDocumentTypesExtensionsFromPath(aURL) of me

return exResList
–> {".INDD", ".INX", ".IDMS", ".INCD", ".INCX", ".ICML", ".INCA", ".ICMA", ".P65", ".T65", ".PMD", ".PMT", ".QXD", ".QXT", ".INDT", ".INDB", ".INDL", ".INLX", ".FLST", ".PRST", ".PDFS", ".joboptions", ".DCST", ".LNST", ".UDC", ".InDesignPlugin", ".PSET", ".SMRD", ".SMWT", ".IDML", ".IDPP", ".INDK", ".INLK", ".CSF", ".ASE", ".ACB", ".ACBL", ".IDPK", ".INJB", ".INRS", ".INCP", ".INDP", ".ICAP", ".IDAP", ".INMS", ".epub"}

on getDocumentTypesExtensionsFromPath(aURL)
  set aRes to getDocumentTypesFromAppURL(aURL) of me
  
  
set exRes to {}
  
repeat with i in (aRes as list)
    set j to (current application’s NSDictionary’s dictionaryWithDictionary:(contents of i))
    
set tmpExt to (j’s valueForKey:"CFBundleTypeExtensions")
    
    
if tmpExt is not equal to missing value then
      set tmpExt to tmpExt as list
      
repeat with ii in tmpExt
        set jj to "." & (contents of ii)
        
if (jj is not in exRes) and (jj is not equal to ".*") and (jj is not equal to ".???") then
          set the end of exRes to jj
        end if
      end repeat
    end if
  end repeat
  
  
return exRes
end getDocumentTypesExtensionsFromPath

on getDocumentTypesFromAppURL(aURL)
  set aBundle to current application’s NSBundle’s bundleWithURL:aURL
  
if aBundle = missing value then return {}
  
set aInfo to aBundle’s infoDictionary()
  
if aInfo = missing value then return {}
  
set aRes to aInfo’s objectForKey:"CFBundleDocumentTypes" –Document Types
  
if aRes = missing value then return {}
  
return aRes
end getDocumentTypesFromAppURL

★Click Here to Open This Script 

Posted in file File path | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSBundle NSDictionary NSURL | Leave a comment

LAN上の別のMacでYouTubeムービー再生をハンズオーバー v2

Posted on 10月 8, 2020 by Takaaki Naganoya

Safariの最前面のウィンドウで再生中のYouTubeムービーの情報を取得し、LAN上の別のMacで再生を引き継ぐ(ハンズオーバーする)AppleScriptです。

macOS 10.13以降、リモートAppleEvents経由でGUIアプリケーションを直接操作する機能が復活しました(Mac OS X 10.7〜10.12ぐらいまでAppleScriptアプレット間のみリモート通信が許可されていた状態)。

メインマシンで再生中のYouTubeムービーを、LAN上の他のマシンに引き継がせてみました。再生を引き継がれる方のマシンでは、システム環境設定の「共有」で「リモートApple Events」の項目をオンにしています(自分のマシンではすべてこの項目をオンにしています)。

(1)リモートマシン上のユーザーのパスワード

AppleScript書類のコメント(Finderコメント)にパスワードを書いておくと、それを読み取って使用するようにしてみました。

(2)リモートマシン上のSafariの起動

リモートマシン上のアプリケーションの操作は、ただリモートマシン上のアプリケーションを指定すればOKなのですが、操作対象のアプリケーションが起動していない場合にはエラーになります。これは、とても困る仕様です。

そこで、リモートマシンのFinder経由でアプリケーションファイルをオープンすることで、リモートマシン上でSafariを起動します。オープン対象をapplication file “Safari”と指定するとエラーになりますが、application file id “com.apple.Safari”と指定するとエラーになりません。

(3)YouTubeで再生中の情報取得

以前調査しておいた内容をそのまま使っています。再生中ならPauseし、再生中の位置(時間)情報を取得し、文字列で指定するために加工してYouTubeのURLに追加しています。URLの加工部分は少々手抜きをしています。

とくに問題なく、メインマシンから他のマシン(macOS 10.15.7/macOS 11.0beta9)にLAN経由で再生をハンズオーバーできました。

実際に、コントロール先のマシン名(Bonjour名)をremoteMachineNameに、ユーザー名をremoteUserNameに、パスワードを実行するAppleScript書類のFinderコメントに書き込んで実行してください。スクリプトエディタ上でもスクリプトメニューからでも問題なく実行できています。

あとは、Safari上のYouTube再生をフルスクリーンで行えるとよいのですが、少し試した範囲ではできなかったので、また地道に調べておく感じでしょうか。

AppleScript名:LAN上の別のMacでYouTubeムービー再生をハンズオーバー v2.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/10/08
—
–  Copyright © 2020 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 remoteMachineName to "MacMini2014.local"
set remoteUserName to "maro"

–ScriptのCommentに書いておいたパスワードを拾って使う
set mePath to (path to me)

–FinderからCommentは拾えるが、メタデータ経由で取得する処理も試してみた
set remoteUserPass to getFinderComment(POSIX path of mePath) of me

tell application "Safari"
  if running then
    tell front document
      set aURL to URL
      
–最前面のウィンドウがYouTubeの場合のみ処理
      
if aURL does not start with "https://www.youtube.com/" then
        display notification "エラー: YouTubeを再生していないため、ハンズオーバーしませんでした"
        
return
      end if
      
      
–再生中のポジションを取得
      
set tRes to (do JavaScript "document.querySelector(’#movie_player video’).currentTime;")
      
      
–再生状況を取得
      
set pRes to (do JavaScript "document.querySelector(’#movie_player video’).paused;")
      
      
if pRes = false then
        –再生中であればPauseする
        
set aRes to (do JavaScript "document.querySelector(’#movie_player .ytp-play-button’).click();")
      end if
      
      
openYouTubeOnRemoteMachine(remoteUserName, remoteUserPass, remoteMachineName, tRes, aURL) of me
    end tell
  end if
end tell

–指定のリモートマシン上のSafariでYouTubeの指定ムービーの指定箇所からの再生を行う
on openYouTubeOnRemoteMachine(remoteUser, remotePass, remoteMachineLocal, newDuration, newURL)
  set remoteMachineName to "eppc://" & remoteUser & ":" & remotePass & "@" & remoteMachineLocal
  
  
–URLの加工。ちょっと手抜きをした
  
if newDuration is not 0 then
    set tText to retTimeText(newDuration) of me
    
if newURL contains "&" then
      set sepChar to "?"
    else
      set sepChar to "&"
    end if
    
    
set newURL to newURL & sepChar & "t=" & tText
  end if
  
  
using terms from application "Safari"
    tell application "Safari" of machine remoteMachineName
      if not running then
        –起動していなかったらあらためてSafariを起動
        
launchRemoteSafari(remoteMachineName) of me
      end if
      
      
try
        close every document
      end try
      
      
set aWin to make new document
      
      
tell aWin
        set URL to newURL
        
–フルスクリーン再生をためしてみたが、こういう書き方ではなかった模様(URLオープンを待つ必要もある)
        
–set aRes to (do JavaScript "document.querySelector(’#movie_player playFullscreen’).click();")
      end tell
      
    end tell
  end using terms from
end openYouTubeOnRemoteMachine

–リモートマシン上でSafariを起動する
on launchRemoteSafari(aMachine)
  using terms from application "Finder"
    tell application "Finder" of machine aMachine
      open application file id "com.apple.Safari"
    end tell
  end using terms from
end launchRemoteSafari

–数値を「h」「m」「s」でフォーマットして返す
on retTimeText(aTime)
  set aHour to aTime div 3600
  
set aMinute to (aTime – (aHour * 3600)) div 60
  
set aSeconds to (aTime mod 60)
  
  
set aString to ""
  
  
if aHour > 0 then
    set aString to aHour & "h"
  end if
  
  
if aMinute > 0 then
    set aString to aString & (aMinute as integer) & "m"
  end if
  
  
if aSeconds > 0 then
    set aString to aString & (aSeconds as integer as string) & "s"
  end if
  
  
return (aString as string)
end retTimeText

–Finderコメントをメタデータ経由で取得
on getFinderComment(aPOSIX)
  set aURL to |NSURL|’s fileURLWithPath:aPOSIX
  
set aMetaInfo to NSMetadataItem’s alloc()’s initWithURL:aURL
  
set metaDict to (aMetaInfo’s valuesForAttributes:{"kMDItemFinderComment"}) as record
  
if metaDict = {} then return ""
  
set aComment to kMDItemFinderComment of (metaDict)
  
return aComment
end getFinderComment

★Click Here to Open This Script 

Posted in Internet JavaScript Remote Control | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy Finder NSMetadataItem NSURL Safari | 14 Comments

PDFにパスワードが設定されている場合には、そのパーミッション情報を取得する

Posted on 8月 31, 2020 by Takaaki Naganoya

指定のPDFをチェックし、オープンするのにパスワードが設定されているとか、コピーが禁止されていないかといったパーミッションを調査するAppleScriptです。

API Lebel in this script Desc
(no api) openPermission document allows opening
allowsCopying copyPermission document allows copying of content to the Pasteboard
allowsPrinting printPermission document allows printing
allowsCommenting commentPermission document allows to create or modify document annotations, including form field entries
allowsContentAccessibility contentAccessPermission document allows to extract content from the document
allowsDocumentAssembly assemblyPermission document allows manage a document by inserting, deleting, and rotating pages
allowsDocumentChanges docchangePermission document allows modify the document contents except for document attributes
allowsFormFieldEntry formPermission document allows modify form field entries even if you can’t edit document annotations

PDFのオープン自体にパスワードロックがかかっている状態を検出するAPIがとくになかったので、いろいろ試行錯誤してOpenをロックしてある状態を検出してみました。

検出は、パスワード未指定でUnlockを試みるというもので、実際にパスワードを設定したPDFを相手に試行錯誤して求めてみました。

このUnlock操作に対してfalseが返ってくることでPDFオープンに対してパスワードが設定されているものと判断しています。

AppleScript名:PDFにパスワードが設定されている場合には、そのパーミッション情報を取得する
— Created 2015-11-02 13:48:32 +0900 by Takaaki Naganoya
— 2015 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "QuartzCore"

set aPath to (choose file of type {"com.adobe.pdf"} with prompt "Select PDF to check")

set aRes to my retProtectedPDFPermissions(aPath)
–>  missing value (パスワードは設定されていない)

–> {openPermission:false, copyPermission:true, printPermission:true, commentPermission:true, contentAccessPermission:true, assemblyPermission:true, docchangePermission:true, formPermission:true}–オープンするためにはパスワードが必要(openPermission)

–指定のPDFにパスワードが設定されているかどうかをチェック
on retProtectedPDFPermissions(aPath)
  set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of aPath)
  
set aPDFdoc to current application’s PDFDocument’s alloc()’s initWithURL:aURL
  
set anEncF to aPDFdoc’s isEncrypted()
  
if anEncF = false then return missing value
  
  
set passLocked to aPDFdoc’s unlockWithPassword:""
  
  
set cpPerm to aPDFdoc’s allowsCopying() as boolean
  
set prPerm to aPDFdoc’s allowsPrinting() as boolean
  
set cmPerm to aPDFdoc’s allowsCommenting() as boolean
  
set caPerm to aPDFdoc’s allowsContentAccessibility() as boolean
  
set daPerm to aPDFdoc’s allowsDocumentAssembly() as boolean
  
set dcPerm to aPDFdoc’s allowsDocumentChanges() as boolean
  
set ffPerm to aPDFdoc’s allowsFormFieldEntry() as boolean
  
  
return {openPermission:passLocked, copyPermission:cpPerm, printPermission:prPerm, commentPermission:cmPerm, contentAccessPermission:caPerm, assemblyPermission:daPerm, docchangePermission:dcPerm, formPermission:ffPerm}
end retProtectedPDFPermissions

★Click Here to Open This Script 

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

アラートダイアログ上にCollection Viewを表示

Posted on 8月 24, 2020 by Takaaki Naganoya

emada2さんからの投稿です。Collection Viewの利用については、Kamenokoの開発時に導入検討したことがありますが、Objective-CのサンプルコードをAppleScriptに翻訳しきれなくて中途半端に放置していました。

まとまった形でCollection Viewのサンプルコードが出てきたのは、おそらくこれが初めてだと思います。ありがとうございます。

Collection Viewを箱庭ダイアログに乗せることができました。
でも残念なのは、itemPrototypeは10.14でDeprecatedになっているので将来的に長く使えないでしょう
その他のやり方を探してappleのサンプルコードをみたら、swift 3で書かれていているので10.14でビルドできませんでした。
なのでまだまだCollection Viewを使いこなすのは難しそうです😢

–> Download Script Bundle

@property (nullable, strong) NSCollectionViewItem *itemPrototype API_DEPRECATED("Use -registerNib:forItemWithIdentifier: or -registerClass:forItemWithIdentifier: instead.", macos(10.5,10.14));
AppleScript名:アラートダイアログ上にCollection Viewを表示
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "AppKit"
use framework "AppleScriptObjC"
use framework "CoreFoundation"
use framework "Foundation"
use scripting additions

on run
  my testRun()
end run

on testRun()
  set aMainMes to "アプリケーションの選択"
  
set aSubMes to "適切なものを以下からえらんでください"
  
  
#ダイアログ内のビューサイズを指定
  
set aHeight to 384
  
set aWidth to 512 + 12 –> スクロールバーの分
  
  
#アプリケーションフォルダ内のアプリケーションを取得
  
set aFolder to path to applications folder
  
set aPath to aFolder’s POSIX path
  
set aURL to current application’s NSURL’s fileURLWithPath:aPath
  
  
# 指定フォルダの直下のファイルを取得
  
set filePaths to current application’s NSFileManager’s defaultManager’s ¬
    contentsOfDirectoryAtURL:aURL ¬
      includingPropertiesForKeys:{current application’s NSURLNameKey} ¬
      
options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) ¬
      
|error|:(missing value)
  
  
set filePaths to filePaths as list
  
set thisFileType to "com.apple.application-bundle"
  
  
set dataSourceList to {}
  
repeat with anItem in filePaths
    
    
set aPath to anItem’s contents’s POSIX path
    
set aURL to (current application’s NSURL’s fileURLWithPath:aPath)
    
    
set {aResult, aUTI, aError} to (aURL’s getResourceValue:(reference) ¬
      forKey:(current application’s NSURLTypeIdentifierKey) ¬
      
|error|:(reference))
    
    
if (aUTI as text) is thisFileType then
      set iconImage to (current application’s NSWorkspace’s sharedWorkspace’s iconForFile:aPath)
      
set dict to {fileURL:aURL, icon:iconImage}
      
set dataSourceList’s end to dict
    end if
  end repeat
  
  
set execButtonTitle to "OK😄"
  
  
set dateObj to my chooseItemByCollectionView:aMainMes subMes:aSubMes myWidth:aWidth myHeight:aHeight myDataSource:dataSourceList okTitle:execButtonTitle
end testRun

on chooseItemByCollectionView:(aMainMes as text) subMes:(aSubMes as text) myWidth:(aWidth as integer) myHeight:(aHeight as integer) myDataSource:(dataSourceList as list) okTitle:(execButtonTitle as text)
  
  
# Script Bundle内のResourcesフォルダを求める
  
set resourcePath to POSIX path of (path to me) & "Contents/Resources/"
  
set theBundle to current application’s NSBundle’s bundleWithPath:resourcePath
  
theBundle’s loadAppleScriptObjectiveCScripts()
  
  
# subClasses.scptを呼び出す
  
set DialogCore to current application’s AppDelegate’s new()
  
set fRes to DialogCore’s chooseItemByCollectionView:aMainMes subMes:aSubMes myWidth:aWidth myHeight:aHeight myDataSource:dataSourceList okTitle:execButtonTitle
  
  
if fRes is missing value then error number -128
  
return fRes as list
end chooseItemByCollectionView:subMes:myWidth:myHeight:myDataSource:okTitle:

★Click Here to Open This Script 

AppleScript名:subClasses
script AppDelegate
  property parent : class "NSObject"
  
  
— IBOutlets
  
property theWindow : missing value
  
  
on applicationShouldTerminate:sender
    return current application’s NSTerminateNow
  end applicationShouldTerminate:
  
  
on applicationShouldTerminateAfterLastWindowClosed:sender
    return true
  end applicationShouldTerminateAfterLastWindowClosed:
  
  
——————
  
  
on applicationWillFinishLaunching:aNotification
    log my testRun()
  end applicationWillFinishLaunching:
  
  
on testRun()
    set aMainMes to "アプリケーションの選択"
    
set aSubMes to "適切なものを以下からえらんでください"
    
    
#ダイアログ内のビューサイズを指定
    
set aHeight to 384
    
set aWidth to 512 + 12 –> スクロールバーの分
    
    
#アプリケーションフォルダ内のアプリケーションを取得
    
set aFolder to path to applications folder
    
set aPath to aFolder’s POSIX path
    
set aURL to current application’s NSURL’s fileURLWithPath:aPath
    
    
# 指定フォルダの直下のファイルを取得
    
set filePaths to current application’s NSFileManager’s defaultManager’s ¬
      contentsOfDirectoryAtURL:aURL ¬
        includingPropertiesForKeys:{current application’s NSURLNameKey} ¬
        
options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) ¬
        
|error|:(missing value)
    
    
set filePaths to filePaths as list
    
set thisFileType to "com.apple.application-bundle"
    
    
set dataSourceList to {}
    
repeat with anItem in filePaths
      
      
set aPath to anItem’s contents’s POSIX path
      
set aURL to (current application’s NSURL’s fileURLWithPath:aPath)
      
      
set {aResult, aUTI, aError} to (aURL’s getResourceValue:(reference) ¬
        forKey:(current application’s NSURLTypeIdentifierKey) ¬
        
|error|:(reference))
      
      
if (aUTI as text) is thisFileType then
        set iconImage to (current application’s NSWorkspace’s sharedWorkspace’s iconForFile:aPath)
        
set dict to {fileURL:aURL, icon:iconImage}
        
set dataSourceList’s end to dict
      end if
    end repeat
    
    
set execButtonTitle to "OK😄"
    
    
set dateObj to my chooseItemByCollectionView:aMainMes subMes:aSubMes myWidth:aWidth myHeight:aHeight myDataSource:dataSourceList okTitle:execButtonTitle
  end testRun
  
  
# アラートダイアログでCollectionViewを表示
  
property _retrieve_data : missing value
  
on chooseItemByCollectionView:(aMainMes as text) subMes:(aSubMes as text) myWidth:(aWidth as integer) myHeight:(aHeight as integer) myDataSource:(dataSourceList as list) okTitle:(execButtonTitle as text)
    
    
set paramObj to {myMessage:aMainMes}
    
set paramObj to paramObj & {mySubMessage:aSubMes}
    
set paramObj to paramObj & {myDataSource:dataSourceList}
    
set paramObj to paramObj & {myWidth:aWidth}
    
set paramObj to paramObj & {myHeight:aHeight}
    
set paramObj to paramObj & {myOKTitile:execButtonTitle}
    
    
my performSelectorOnMainThread:"raizeAlert:" withObject:paramObj waitUntilDone:true
    
    
if (my _retrieve_data) is missing value then error number -128
    
return (my _retrieve_data)
  end chooseItemByCollectionView:subMes:myWidth:myHeight:myDataSource:okTitle:
  
  
## retrieve date
  
on raizeAlert:paramObj
    
    
set mesText to paramObj’s myMessage as text
    
set infoText to paramObj’s mySubMessage as text
    
set dataSourceList to paramObj’s myDataSource as list
    
set viewWidth to paramObj’s myWidth as integer
    
set viewHeight to paramObj’s myHeight as integer
    
set okButton to paramObj’s myOKTitile as text
    
    
# set up view
    
set {thisView, collectionView} to my makeContentView(dataSourceList, viewWidth, viewHeight)
    
    
### set up alert
    
tell current application’s NSAlert’s new()
      addButtonWithTitle_(okButton)
      
addButtonWithTitle_("Cancel")
      
setAccessoryView_(thisView)
      
–setIcon_(aImage)
      
setInformativeText_(infoText)
      
setMessageText_(mesText)
      
tell |window|()
        setInitialFirstResponder_(thisView)
      end tell
      
#### show alert in modal loop
      
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
    end tell
    
    
### retrieve data
    
set aIndexSet to collectionView’s selectionIndexes()
    
set theArray to current application’s NSArrayController’s alloc()’s initWithContent:(collectionView’s content())
    
set chooseItems to ((theArray)’s arrangedObjects()’s objectsAtIndexes:aIndexSet) as list
    
    
# reset
    
set _retrieve_data to {}
    
    
repeat with anItem in chooseItems
      set (my _retrieve_data)’s end to anItem’s contents
    end repeat
    
    
return my _retrieve_data
  end raizeAlert:
  
  
# set up view
  
on makeContentView(dataSourceList, viewWidth, viewHeight)
    
    
set thisRect to current application’s NSMakeRect(0, 0, viewWidth, viewHeight)
    
    
tell current application’s NSCollectionView’s alloc
      tell initWithFrame_(thisRect)
        setItemPrototype_(current application’s YKZCollectionViewItem’s new())
        
        
setAllowsEmptySelection_(true)
        
setAllowsMultipleSelection_(true)
        
setContent_(dataSourceList)
        
setSelectable_(true)
        
        
set collectionView to it
      end tell
    end tell
    
    
# Viewを作成
    
tell current application’s NSScrollView’s alloc()
      tell initWithFrame_(thisRect)
        setBorderType_(current application’s NSBezelBorder)
        
setDocumentView_(collectionView)
        
setHasHorizontalScroller_(true)
        
setHasVerticalScroller_(true)
        
set baseView to it
      end tell
    end tell
    
    
return {baseView, collectionView}
  end makeContentView
end script

#MARK: –
script YKZView
  property parent : class "NSView"
  
  
property imageView : missing value
  
  
property selected : false
  
property viewItemHeight : 128
  
property viewItemWidth : 128
  
  
on initWithFrame:rect
    
    
set myRect to current application’s NSMakeRect(0, 0, viewItemWidth, viewItemHeight)
    
continue initWithFrame:myRect
    
    
set aHeight to viewItemHeight – 10
    
set aWidth to viewItemWidth – 10
    
set newRect to current application’s NSMakeRect(5, 5, aWidth, aHeight)
    
    
tell current application’s NSImageView’s alloc()
      tell initWithFrame_(newRect)
        setImageScaling_(current application’s NSImageScaleProportionallyUpOrDown)
        
setImageAlignment_(current application’s NSImageAlignCenter)
        
set my imageView to it
      end tell
    end tell
    
    
my addSubview:(my imageView)
    
return me
  end initWithFrame:
  
  
on drawRect:rect
    if my selected then
      current application’s NSColor’s redColor()’s |set|()
    else
      current application’s NSColor’s controlBackgroundColor()’s |set|()
    end if
    
current application’s NSRectFill(rect)
    
continue drawRect:rect
  end drawRect:
end script

#MARK: –
script YKZCollectionViewItem
  property parent : class "NSCollectionViewItem"
  
  
on loadView()
    my setView:(current application’s YKZView’s alloc()’s initWithFrame:(current application’s NSZeroRect))
  end loadView
  
  
on setRepresentedObject:representedObject
    –continue setRepresentedObject:representedObject
    
if representedObject is missing value then return
    (
my view())’s imageView’s setImage:(representedObject’s icon)
  end setRepresentedObject:
  
  
on setSelected:isSelected
    set (my view())’s selected to isSelected
    (
my view())’s setNeedsDisplay:true
    
continue setSelected:isSelected
  end setSelected:
end script

★Click Here to Open This Script 

Posted in dialog file File path | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSAlert NSArrayController NSBundle NSCollectionView NSCollectionViewItem NSColor NSFileManager NSImageView NSURL NSWorkspace | Leave a comment

指定アプリケーションのバイナリアーキテクチャ判定 v3

Posted on 8月 18, 2020 by Takaaki Naganoya

指定のアプリケーションファイルが32bitバイナリかどうかを判定するAppleScriptの改訂版です。

本Scriptシリーズは対象のアプリケーションが起動している状態で対象CPUアーキテクチャを判定するものではなく、起動していない状態で判定するものです。

PPC 32 / PPC 64 / Intel 32 / Intel 64 / ARM 64の各アーキテクチャの判定を行います。FAT Binaryの場合には複数のアーキテクチャが返ってきます。


▲本Scriptの応用例

AppleScript名:指定アプリケーションのバイナリアーキテクチャ判定 v3.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/08/18
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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

set anApp to choose file of type {"com.apple.application-bundle"} default location (path to applications folder)
–set anApp to choose file default location (path to applications folder)
set app32 to chk32Binary(anApp) of me
–> {"PPC 32", "Intel x32"}

on chk32Binary(anAlias)
  set aPOSIX to POSIX path of anAlias
  
set aURL to current application’s |NSURL|’s fileURLWithPath:(aPOSIX)
  
set aBundle to current application’s NSBundle’s bundleWithURL:aURL
  
  
set F1 to (aBundle = missing value) as boolean
  
if F1 is not equal to false then return calcIllegular(aPOSIX) of me
  
  
set aDict to aBundle’s executableArchitectures()
  
set F2 to (aDict = missing value) as boolean
  
if F2 is equal to true then return calcIllegular(aPOSIX) of me
  
  
set aList to (aDict’s valueForKeyPath:"stringValue") as list
  
  
set arList to {}
  
repeat with i in aList
    set j to contents of i
    
    
if j = "7" then –NSBundleExecutableArchitectureI386
      set the end of arList to "Intel 32"
    else if j = "18" then –NSBundleExecutableArchitecturePPC
      set the end of arList to "PPC 32"
    else if j = "16777223" then –NSBundleExecutableArchitectureX86_64
      set the end of arList to "Intel 64"
    else if j = "16777234" then –NSBundleExecutableArchitecturePPC64
      set the end of arList to "PPC 64"
    else if j = "16777228" then –NSBundleExecutableArchitectureARM64
      set the end of arList to "ARM 64"
    end if
  end repeat
  
  
return arList
end chk32Binary

on calcIllegular(aPOSIX)
  –Old Style Bundle for PPC
  
set sRes to do shell script "file " & quoted form of aPOSIX
  
if sRes contains "Mach-O executable ppc" then return {"PPC 32"} –NSBundleExecutableArchitecturePPC
  
return missing value
end calcIllegular

★Click Here to Open This Script 

Posted in file File path | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSBundle NSURL | Leave a comment

指定アプリケーションのバイナリアーキテクチャ判定

Posted on 8月 18, 2020 by Takaaki Naganoya

指定のアプリケーションファイルが32bitバイナリかどうかを判定するAppleScriptです。

実行ファイルのパスを求めて、fileコマンドで情報を取得します。本Scriptでは32bitバイナリであるかどうかの判定のみ行なっていますが、CPUアーキテクチャに着目してPPC/Intel 32/Intel 64/ARMの判定を行えるように作り込めます。

また、アプリケーションでありながらもバンドル形式になっていない実行ファイル(古い時代のファイルだったり、サードパーティの開発環境だったり)も存在しているため、NSBundleを求めたときにmissing valueが返ってくることもあります。

1ファイルだけ処理できてもあまり意味がないので、指定フォルダ以下のすべてのアプリケーションファイルをリストアップして、ループで処理するようなことをやることになるでしょう。

--PPC 32bit
/Applications/okaeri_up/v2.0b/Okaeri.app/Contents/MacOS/Okaeri: Mach-O executable ppc

--PPC/Intel 32bit FAT Binary
/Applications/Adobe Illustrator CS3/Adobe Illustrator.app//Contents/MacOS/Adobe Illustrator: Mach-O universal binary with 2 architectures: [i386:Mach-O executable i386] [ppc]
/Applications/Adobe Illustrator CS3/Adobe Illustrator.app//Contents/MacOS/Adobe Illustrator (for architecture i386):	Mach-O executable i386
/Applications/Adobe Illustrator CS3/Adobe Illustrator.app//Contents/MacOS/Adobe Illustrator (for architecture ppc):	Mach-O executable ppc

--Intel 32
/Applications/Adobe Illustrator CS5/Adobe Illustrator.app//Contents/MacOS/Adobe Illustrator: Mach-O executable i386

--Intel 32/64bit FAT Binary
/Applications/System Preferences.app//Contents/MacOS/System Preferences: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [i386:Mach-O executable i386]
/Applications/System Preferences.app//Contents/MacOS/System Preferences (for architecture x86_64):	Mach-O 64-bit executable x86_64
/Applications/System Preferences.app//Contents/MacOS/System Preferences (for architecture i386):	Mach-O executable i386

--Intel 64
/Applications/Adobe Illustrator 2020/Adobe Illustrator.app//Contents/MacOS/Adobe Illustrator: Mach-O 64-bit executable x86_64

--Intel/PPC 64bit FAT Binary
/Applications/Geekbench 2.1/Geekbench (64-bit).app/Contents/MacOS/Geekbench: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [ppc64]
/Applications/Geekbench 2.1/Geekbench (64-bit).app/Contents/MacOS/Geekbench (for architecture x86_64):	Mach-O 64-bit executable x86_64
/Applications/Geekbench 2.1/Geekbench (64-bit).app/Contents/MacOS/Geekbench (for architecture ppc64):	Mach-O executable ppc64

--Intel 64/ARM 64 FAT Binary
/System/Applications/Utilities/Script Editor.app/Contents/MacOS/Script Editor: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
/System/Applications/Utilities/Script Editor.app/Contents/MacOS/Script Editor (for architecture x86_64):	Mach-O 64-bit executable x86_64
/System/Applications/Utilities/Script Editor.app/Contents/MacOS/Script Editor (for architecture arm64e):	Mach-O 64-bit executable arm64e
AppleScript名:指定アプリケーションのバイナリアーキテクチャ判定.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/08/18
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

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


set anApp to choose file of type {“com.apple.application-bundle”} default location (path to applications folder)


set app32 to chk32Binary(anApp) of me
–> true –32bit
–> false –64bit


on chk32Binary(anApp)
  set aExec to getAppPropertyFromInfoPlist(anApp, “CFBundleExecutable”) of me
set posixPath to (POSIX path of anApp) & “/Contents/MacOS/” & aExec


  set aRes to do shell script “file “ & quoted form of posixPath
log aRes
if aRes contains “Mach-O executable i386” then
    return true –32bit
  else
    return false –64bit
  end if
end chk32Binary


on getAppPropertyFromInfoPlist(anAlias, aKey as string)
  set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of anAlias)
set aBundle to current application’s NSBundle’s bundleWithURL:aURL
set aDict to aBundle’s infoDictionary()
set vRes to (aDict’s valueForKey:aKey) as string
return vRes as string
end getAppPropertyFromInfoPlist


★Click Here to Open This Script 
Posted in file File path | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSBundle NSURL | Leave a comment

アラートダイアログでMarkdownをプレビュー v3a

Posted on 8月 15, 2020 by Takaaki Naganoya

edama2さんからの投稿です。Markdown書類をプレビューするAppleScriptです。

–> Download Script bundle

本Scriptには、画期的な技術が用いられています。普通、AppleScriptObjCでは、WkWebViewによるネットワークアクセスを行うと、ネットワークアクセスを行う子プロセスと、WkWebViewで表示を行う子プロセスが生じて、処理が終了しても残ってしまいます。

スクリプトエディタ上で実行した場合には、スクリプトエディタを終了すればこうした子プロセスはメモリ上からパージされますが、終了するまで子プロセスは残されたままです。

「まー、AppleScriptObjCだから仕方ないよねー」

などと去年の年末に秋葉原でカレーを食べながらバカ話をしていた記憶がありますが、edama2さんがついにこれを回避する記法を編み出してしまったわけです。

回避方法については、やり方を説明されると「なるほど」という単純明快なものです(それを思いつくのがすごい)。osascript経由でAppleScriptを再実行することで、ランタイム環境を明示的にosascriptに指定してプログラムを実行。osascriptの終了とともにメモリ上から子プロセスがパージされるということのようです。

ランタイム環境を動的に生成し、実行後にランタイム環境がメモリ上から消去されるようになっていれば、子プロセス(ゴミ)が残っていてもまとめて消える………AppleScriptのランタイム環境はosascriptのほかCocoa系もあるので、そちらでも試してみるとよいでしょう。

ほかにも、WkWebViewによる表示を行う専用の支援アプリケーションを作っておき、AppleScript用語辞書経由で表示を行わせるという解決策も考えられます(ためしていないですが)。

AppleScript名:アラートダイアログでMarkdownをプレビュー v3a
use AppleScript
use framework “AppKit”
use framework “Foundation”
use framework “WebKit”
use scripting additions

on run args
  if (args’s class) is script then
    set myPath to (path to me)’s POSIX path
set resultText to do shell script “osascript “ & myPath’s quoted form
  else
    my main()
  end if
end run


on main()
  set mes to “Markdownファイルを選択してください。”
set chooseItems to choose file of type {“net.daringfireball.markdown”} with prompt mes


  #
set aScreen to current application’s NSScreen’s mainScreen()
set screenFrame to aScreen’s frame()
set aHeight to current application’s NSHeight(screenFrame)
set aWidth to current application’s NSWidth(screenFrame)
set aHeight to aHeight * 0.845
set aWidth to aWidth * 0.94 / 2


  set paramObj to {myMessage:“Markdown preview”}
set paramObj to paramObj & {mySubMessage:“file : “ & chooseItems’s POSIX path}
set paramObj to paramObj & {mdFile:chooseItems}
set paramObj to paramObj & {viewWidth:aWidth}
set paramObj to paramObj & {viewHeight:aHeight}


  my performSelectorOnMainThread:“displayMarkdownPreview:” withObject:(paramObj) waitUntilDone:true
end main


# Markdownをダイアログで表示
on displayMarkdownPreview:paramObj
  set mesText to myMessage of paramObj as text
set infoText to mySubMessage of paramObj as text
set mdFile to (mdFile of paramObj) as alias
set viewWidth to (viewWidth of paramObj) as integer
set viewHeight to (viewHeight of paramObj) as integer


  ## HTMLを読み込む
set mePath to path to me
set resPath to (mePath & “Contents:Resources:index.html”) as text
set htmlStr to (read (resPath as alias) as «class utf8») as text


  ## MDを読み込む
set mdStr to (read mdFile as «class utf8») as text


  ## MD内に改行があるとうまく読み込まれないので改行を置き換え
set mdStr to current application’s NSString’s stringWithString:mdStr
set mdStr to mdStr’s stringByReplacingOccurrencesOfString:(linefeed) withString:“\\n”
set mdStr to mdStr’s stringByReplacingOccurrencesOfString:“’” withString:“\\’”


  ## html内の文字を置き換え
set aString to current application’s NSString’s stringWithFormat_(htmlStr, mdStr) as text
log result


  ## baseURL
set aPath to mdFile’s POSIX path
set baseURL to current application’s NSURL’s fileURLWithPath:aPath
set baseURL to baseURL’s URLByDeletingLastPathComponent()


  ##
set aConf to current application’s WKWebViewConfiguration’s new()
tell current application’s WKUserContentController’s new()
    aConf’s setUserContentController:it
  end tell


  ## WebViewを作成&読み込み
set frameSize to current application’s NSMakeRect(0, 0, viewWidth, viewHeight)
tell current application’s WKWebView’s alloc()
    tell initWithFrame_configuration_(frameSize, aConf)
      setNavigationDelegate_(me)
setUIDelegate_(me)
loadHTMLString_baseURL_(aString, baseURL)
set theView to it
    end tell
  end tell


  ## アイコンの指定
set aImage to current application’s NSWorkspace’s sharedWorkspace()’s iconForFileType:“net.daringfireball.markdown”


  current application’s NSRunningApplication’s currentApplication()’s activateWithOptions:0


  ## set up alert
tell current application’s NSAlert’s new()
    addButtonWithTitle_(“Close”)
setAccessoryView_(theView)
setIcon_(aImage)
setInformativeText_(infoText)
setMessageText_(mesText)
tell |window|()
      setInitialFirstResponder_(theView)
    end tell
### show alert in modal loop
if runModal() is (current application’s NSAlertSecondButtonReturn) then return
  end tell


  ## 後始末
theView’s stopLoading()
set js to “window.open(’about:blank’,’_self’).close();”
theView’s evaluateJavaScript:js completionHandler:(missing value)
set theView to missing value
end displayMarkdownPreview:

★Click Here to Open This Script 
Posted in dialog Markdown URL | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSAlert NSAlertSecondButtonReturn NSRunningApplication NSScreen NSString NSURL NSWorkspace WKUserContentController WKWebView WKWebViewConfiguration | Leave a comment

指定ファイルのメタデータ表示 v2

Posted on 7月 7, 2020 by Takaaki Naganoya

指定ファイルのメタデータをTable UIで表示するAppleScriptです。

テーブル表示用のライブラリ「display table by list」のアップデート版(v1.3)を利用しています。同ライブラリは特定用途のために間に合わせで作っただけだったので、バグを直して汎用性を高めました(無駄なウィンドウの半透明表示をやめた)。また、アイコン表示用のURL指定を省略したときにエラーになる点を修正しました。

–> Download displayTable.scptd(AppleScript Library)

データのテキスト化ライブラリ「everythingToText」も利用していますが、これソースもオープンにしていてよく使っているのですが、とくに決まったURLから配布というのは行なっていませんね。機能が素朴すぎてライブラリとして配布するのがためらわれるところです。

–> Download displayMetadataByTablev2

というわけで、ライブラリを含んだScript Bundleをダウンロードできるようにしておきました(↑)。

AppleScript名:指定ファイルのメタデータ表示 v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/07/07
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions
use easyTable : script "display table by list"
use eText : script "everythingToTextKit"

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

set aFile to choose file
set aPOSIX to POSIX path of aFile
set aURL to |NSURL|’s fileURLWithPath:(aPOSIX)
set aMetaInfo to NSMetadataItem’s alloc()’s initWithURL:aURL
set attrList to (aMetaInfo’s attributes()) as list

set aList to {}
repeat with i in attrList
  set metaDict to (aMetaInfo’s valuesForAttributes:{i as string}) as record
  
set recStr to convToStr(metaDict) of eText
  
set the end of aList to {i as string, recStr as string}
  
–set the end of aList to (recStr as string)
end repeat

set fLabels to {"Attribute", "Value"}

set aRes to (display table by list aList main message "Metadata" sub message aPOSIX size {1200, 800} labels fLabels)

★Click Here to Open This Script 

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

指定のKeynote書類のタイプを求める

Posted on 7月 5, 2020 by Takaaki Naganoya

指定のKeynote書類がフラット形式かバンドル形式かを取得するAppleScriptです。

UTIを取得してみたら簡単に判定できたので、これでいいでしょう。バンドル形式かどうかを判定するために、ディレクトリ構造を仮定したパスを組み立てて、アクセスしてみることも検討したのですが、UTIを求めるだけですみました。

AppleScript名:指定のKeynote書類のタイプを求める.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/07/05
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aFilePath to choose file of type {"com.apple.iwork.keynote.key", "com.apple.iwork.keynote.sffkey"}
set aUTI to getUTIfromPath(aFilePath) of me
–> "com.apple.iwork.keynote.key" (Bundle)
–> "com.apple.iwork.keynote.sffkey" (Flat)

on getUTIfromPath(anAlias)
  set aPOSIXpath to POSIX path of anAlias
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aPOSIXpath
  
if aURL = missing value then return ""
  
set aRes to aURL’s resourceValuesForKeys:{current application’s NSURLTypeIdentifierKey} |error|:(missing value)
  
if aRes = missing value then return ""
  
return (aRes’s NSURLTypeIdentifierKey) as string
end getUTIfromPath

★Click Here to Open This Script 

AppleScript名:指定のKeynote書類のタイプを求める v2.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/07/05
—
–  Copyright © 2020 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aFilePath to choose file of type {"com.apple.iwork.keynote.key", "com.apple.iwork.keynote.sffkey"}
set aUTI to getUTIfromPath(aFilePath) of me
set aKType to retKeynoteBundleOrNot(aUTI) of me
–> "Flat Keynote"
–> "Bundle Package Keynote"

on retKeynoteBundleOrNot(anUTI)
  if anUTI = "com.apple.iwork.keynote.sffkey" then
    return "Flat Keynote"
  else if anUTI = "com.apple.iwork.keynote.key" then
    return "Bundle Package Keynote"
  else
    return false
  end if
end retKeynoteBundleOrNot

on getUTIfromPath(anAlias)
  set aPOSIXpath to POSIX path of anAlias
  
set aURL to current application’s |NSURL|’s fileURLWithPath:aPOSIXpath
  
if aURL = missing value then return ""
  
set aRes to aURL’s resourceValuesForKeys:{current application’s NSURLTypeIdentifierKey} |error|:(missing value)
  
if aRes = missing value then return ""
  
return (aRes’s NSURLTypeIdentifierKey) as string
end getUTIfromPath

★Click Here to Open This Script 

Posted in file UTI | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy NSURL NSURLTypeIdentifierKey | Leave a comment

アラートダイアログ上にWebViewでCesiumを用いて地球儀上に地図を表示

Posted on 6月 21, 2020 by Takaaki Naganoya

アラートダイアログ上にWkWebViewを敷いて、Cesiumを呼び出して地球儀上に地図を表示するAppleScriptです。

もともとのコードは国土地理院のサイトで見つけたものなのですが、地球儀上の地図表示が行えるようです。地球儀以外にも、メルカトル図法などの地図っぽい表示も可能。

右上のメニューから、地図の種類を選択して指定できます。マウスホイールでズームします。

白黒で表示された地図がなかなか面白いところです。

AppleScript名:アラートダイアログ上にWebViewでCesiumを用いて地球儀上に地図を表示.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/06/20
—
–  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 "WebKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSAlert : a reference to current application’s NSAlert
property NSString : a reference to current application’s NSString
property NSButton : a reference to current application’s NSButton
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property WKUserContentController : a reference to current application’s WKUserContentController
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property returnCode : 0

–set aRecArray to {{5, 20}, {25, 67}, {85, 21}, {100, 33}, {220, 88}, {250, 50}, {330, 95}, {410, 12}, {475, 44}, {480, 90}}
–set jsonStr to array2DToJSONArray(aRecArray) of me

–https://github.com/gsi-cyberjapan/gsitiles-cesium/blob/gh-pages/index.html
set myStr to "<!DOCTYPE html>
<html>
<head>
<meta charset=\"UTF-8\">
<title>GSI Tiles on Cesium</title>
<script src=\"https://cesium.com/downloads/cesiumjs/releases/1.63.1/Build/Cesium/Cesium.js\"></script>
<link href=\"https://cesium.com/downloads/cesiumjs/releases/1.63.1/Build/Cesium/Widgets/widgets.css\" rel=\"stylesheet\">
<style>
#cesiumContainer {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
margin: 0;
overflow: hidden;
padding: 0;
font-family: sans-serif;
}
html {
height: 100%;
}
body {
padding: 0;
margin: 0;
overflow: hidden;
height: 100%;
}
</style>
</head>

<body>
<div id=\"cesiumContainer\"></div>
<script>
var viewer = new Cesium.Viewer(’cesiumContainer’, {
imageryProvider: new Cesium.createOpenStreetMapImageryProvider({
url: ’https://cyberjapandata.gsi.go.jp/xyz/std/’,
credit: new Cesium.Credit(’地理院タイル’, ’’, ’https://maps.gsi.go.jp/development/ichiran.html’)
}),
baseLayerPicker: true,
geocoder: false,
homeButton: false
});

viewer.camera.setView({
destination : Cesium.Cartesian3.fromDegrees(140.00, 36.14, 20000000.0)
});
</script>
</body>
</html>"

–set aString to current application’s NSString’s stringWithFormat_(myStr, jsonStr) as string

set paramObj to {myMessage:"Cesium Test", mySubMessage:"This is a Cesium test", htmlStr:myStr}
–my browseStrWebContents:paramObj–for debug
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true

on browseStrWebContents:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set htmlString to (htmlStr of paramObj)
  
  
set aWidth to 1220
  
set aHeight to 720
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定HTML内のJavaScriptをFetch
  
set jsSource to pickUpFromToStr(htmlString, "<script>", "</script>") of me
  
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
using terms from scripting additions
    set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
  end using terms from
  
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aWebView
    
    
set myWindow to its |window|
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
if (my returnCode as number) = 1001 then error number -128
end browseStrWebContents:

on doModal:aParam
  set (my returnCode) to (aParam’s runModal()) as number
end doModal:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
return cStr as string
end pickUpFromToStr

–リストを任意のデリミタ付きでテキストに
on retArrowText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retArrowText

on array2DToJSONArray(aList)
  set anArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

on parseByDelim(aData, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set dList to text items of aData
  
set AppleScript’s text item delimiters to curDelim
  
return dList
end parseByDelim

★Click Here to Open This Script 

Posted in dialog JavaScript JSON | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSRunningApplication NSString NSURL NSURLRequest NSUTF8StringEncoding WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

アラートダイアログ上のWebViewでGio.jsを用いて地球儀+データアニメーション表示 v2a

Posted on 6月 20, 2020 by Takaaki Naganoya

アラートダイアログ上にWkWebViewを配置し、その上にGio.jsによる地球儀+グラフデータ(アニメーション)を表示するAppleScriptです。

Gio.jsは国際間の貿易収支、Webトラフィックなどの可視化を行うさいに利用するライブラリのようです。さまざまな可視化サンプルをかき集めて、実際にWkWebViewで表示できたもののうち、再利用しやすく利用価値の高そうなものということで、このGio.jsを選んでみました。

派手なビジュアライズの部品はいろいろ見て回りましたが、割とすぐ慣れるというか、飽きるというべきなのか、派手に見せたところで実用性はさほど高まらないといった悟りを得るに至った次第です。

本ScriptはAppleScript側のオブジェクトで与えたデータをもとに表示内容を変更するところまでは作り込んでいませんが、一応、Gio.jsをCDN上のものを呼び出すように書き換えておきました。

AppleScript名:アラートダイアログ上にWebViewでGio.jsを用いて地球儀上にデータアニメーションを表示 v2a.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/06/20
—
–  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 "WebKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSAlert : a reference to current application’s NSAlert
property NSString : a reference to current application’s NSString
property NSButton : a reference to current application’s NSButton
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property WKUserContentController : a reference to current application’s WKUserContentController
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property returnCode : 0

–set aRecArray to {{5, 20}, {25, 67}, {85, 21}, {100, 33}, {220, 88}, {250, 50}, {330, 95}, {410, 12}, {475, 44}, {480, 90}}
–set jsonStr to array2DToJSONArray(aRecArray) of me

–https://github.com/webhacck/gio-js-sample/blob/master/sample2.html
set myStr to "<!DOCTYPE html>
<html>
<head>
<title>Gio.js sample</title>
<meta charset=\"utf-8\">
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
</head>
<body>

<div id=\"globalArea\" style=\"width:1000px;height:700px\"></div>

  <script src=\"https://threejs.org/build/three.min.js\"></script>
  <script src=\"https://cdn.jsdelivr.net/npm/giojs@2.2.1/build/gio.min.js\"></script>
  <script>

var container = document.getElementById( \"globalArea\" );

//config用のオプション設定
var config = {
\"control\": {
\"stats\": false,
\"disableUnmentioned\": false,
\"lightenMentioned\": true,
\"inOnly\": false,
\"outOnly\": false,
\"initCountry\": \"JP\",
\"halo\": true
},
\"color\": {
\"surface\": 1744048,
\"selected\": 2141154,
\"in\": 16765762,
\"out\": 5366382,
\"halo\": 2141154,
\"background\": 0
},
\"brightness\": {
\"ocean\": 0.95,
\"mentioned\": 0.28,
\"related\": 0.74
}
};


//dataset
var data= [
{ e: \"JP\", i: \"CN\", v: 8000000 },
{ e: \"JP\", i: \"TH\", v: 3000000 },
{ e: \"JP\", i: \"ID\", v: 1000000 },
{ e: \"US\", i: \"JP\", v: 8000000 },
{ e: \"RU\", i: \"JP\", v: 3000000 },
{ e: \"IN\", i: \"JP\", v: 1000000 }]

//第2引数にconfigを設定する
var controller = new GIO.Controller( container, config );

controller.addData( data );
controller.init();

</script>

</body>
</html>"

–set aString to current application’s NSString’s stringWithFormat_(myStr, jsonStr) as string

set paramObj to {myMessage:"Gio.js Test", mySubMessage:"This is a Gio.js test", htmlStr:myStr}
–my browseStrWebContents:paramObj–for debug
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true

on browseStrWebContents:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set htmlString to (htmlStr of paramObj)
  
  
set aWidth to 1020
  
set aHeight to 720
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定HTML内のJavaScriptをFetch
  
set jsSource to pickUpFromToStr(htmlString, "<script>", "</script>") of me
  
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
using terms from scripting additions
    set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
  end using terms from
  
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aWebView
    
    
set myWindow to its |window|
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
if (my returnCode as number) = 1001 then error number -128
end browseStrWebContents:

on doModal:aParam
  set (my returnCode) to (aParam’s runModal()) as number
end doModal:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
return cStr as string
end pickUpFromToStr

–リストを任意のデリミタ付きでテキストに
on retArrowText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retArrowText

on array2DToJSONArray(aList)
  set anArray to current application’s NSMutableArray’s arrayWithArray:aList
  
set jsonData to current application’s NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

on parseByDelim(aData, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set dList to text items of aData
  
set AppleScript’s text item delimiters to curDelim
  
return dList
end parseByDelim

★Click Here to Open This Script 

Posted in dialog | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSRunningApplication NSString NSURL NSURLRequest NSUTF8StringEncoding WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

指定のPDFからTOCを削除する

Posted on 6月 19, 2020 by Takaaki Naganoya

指定のPDFからTOCを削除して別名保存するAppleScriptです。

TOCを削除した新規ファイルは元ファイルと同じ場所に枝番つきで重複回避しつつ作成されます。

33MBで257ページほどあるPDFで、詳細に目次から記事へのリンクを手動で作成し、TOCを手動で作ったところ、Adobe Actobatが、

などというメッセージを出して保存ができなかったので、中途半端に1個だけエントリが残ったTOCを削除するために用意したものです(既存のScriptに機能があったので、削除機能だけ抽出)。

AppleScript名:指定のPDFからTOCを削除する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2018/08/18
—
–  Copyright © 2018 Piyomaru Software, All Rights Reserved
—

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

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

–PDFの指定
set aFile to choose file of type {"pdf"} with prompt (progress additional description)
set filePath to aFile’s POSIX path

set fileURL to |NSURL|’s fileURLWithPath:filePath
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:fileURL
set parentOL to aPDFdoc’s outlineRoot()

if parentOL is equal to missing value then
  display dialog "There is no TOC with this PDF"
  
return
end if

set outLineCount to parentOL’s numberOfChildren()
set outLineCount to outLineCount as number

–既存のTOCを削除
repeat with num from (outLineCount – 1) to 0 by -1
  (parentOL’s childAtIndex:num)’s removeFromParent()
end repeat

–保存
set progress description to "PDFを保存"
set progress additional description to "保存先の確認"
tell (NSString’s stringWithString:filePath)
  set parentPath to stringByDeletingLastPathComponent() –>親フォルダ
  
set longFileName to lastPathComponent() –>拡張子ありの名前
end tell
set newFilePath to my pathExists(parentPath, longFileName)

set progress additional description to "書き出し中: " & newFilePath
set newFileURL to |NSURL|’s fileURLWithPath:newFilePath
aPDFdoc’s writeToURL:newFileURL

–名前が重複していないか確認
on pathExists(currentPath as text, fileName as text)
  tell (NSString’s stringWithString:fileName)
    set shortFileName to stringByDeletingPathExtension() as text
    
set aSuffix to pathExtension() as text
  end tell
  
if aSuffix ≠ "" then set aSuffix to "." & aSuffix as text
  
  
set currentPath to NSString’s stringWithString:currentPath
  
set unkonwnPath to (currentPath’s stringByAppendingPathComponent:fileName)
  
  
set aSpace to space
  
set num to 0
  
  
repeat while ((NSFileManager’s defaultManager)’s fileExistsAtPath:unkonwnPath)
    set num to num + 1
    
set tmpName to shortFileName & aSpace & num & aSuffix as text
    
set unkonwnPath to (currentPath’s stringByAppendingPathComponent:tmpName)
  end repeat
  
  
return (unkonwnPath as text)
end pathExists

★Click Here to Open This Script 

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

アラートダイアログ上にWebViewでChart.jsを表示(Pie Chart) v2

Posted on 6月 15, 2020 by Takaaki Naganoya

Chart.jsを用いてアラートダイアログ上に円グラフを表示するAppleScriptです。

サンプルの円グラフの色使いがいまひとつだったので、Keynoteのグラフの色設定を読み取って利用しています。

ネットワーク接続チェックなど、必要な処理はひととおり行なっている、実戦レベルに近いものです。

さまざまなJavaScript系のグラフ表示のプログラムを試してみましたが、Chart.jsはとてもいい感じです。

AppleScript名:アラートダイアログ上にWebViewでChart.jsを表示(Pie Chart) v2.scptd
set aList to {"field1", "field2", "field3", "field4", "field5"}
set cList to {38, 31, 21, 10, 1}
set dMes1 to "Pie chart Test"
set dMes2 to "This is a simple Donut chart using charts.js"

displayPieChart(aList, cList, dMes1, dMes2) of pieChartLib

script pieChartLib
  —
  
–  Created by: Takaaki Naganoya
  
–  Created on: 2020/06/12
  
—
  
–  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 "WebKit"
  
use scripting additions
  
  
property |NSURL| : a reference to current application’s |NSURL|
  
property NSAlert : a reference to current application’s NSAlert
  
property NSString : a reference to current application’s NSString
  
property NSButton : a reference to current application’s NSButton
  
property WKWebView : a reference to current application’s WKWebView
  
property WKUserScript : a reference to current application’s WKUserScript
  
property NSURLRequest : a reference to current application’s NSURLRequest
  
property NSMutableArray : a reference to current application’s NSMutableArray
  
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
  
property NSRunningApplication : a reference to current application’s NSRunningApplication
  
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
  
property WKUserContentController : a reference to current application’s WKUserContentController
  
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
  
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd
  
  
property returnCode : 0
  
property parent : AppleScript
  
  
  
on displayPieChart(aList, cList, dMes1, dMes2)
    –Error Check
    
if length of aList is not equal to length of cList then error "Wrong Parameter items"
    
if length of aList > 18 then error "Too much items"
    
    
set aRes to hasInternetConnection("https://cdnjs.cloudflare.com") of me
    
if aRes = false then error "Internet connection lost"
    
    
–Data convert to JSON strings
    
set aJsonStr to array2DToJSONArray(aList) of me
    
set cJsonStr to array2DToJSONArray(cList) of me
    
    
–Pie Chart Template HTML
    
set myStr to "<!DOCTYPE html>
<html lang=\"ja\">
<head>
<meta charset=\"utf-8\">
 <title>Graph</title>
</head>
<body>
<canvas id=\"myPieChart\"></canvas>
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.js\"></script>

<script>
var ctx = document.getElementById(\"myPieChart\");
var myPieChart = new Chart(ctx, {
type: ’pie’,
data: {
labels: %@,
datasets: [{
backgroundColor: %@,
data: %@
}]
},
options: {
title: {
display: false,
text: \"\"
}
}
});
</script>
</body>
</html>"

    –Color Table From Apple Keynote
    
set bList to {"#118EFE", "#53D529", "#F5AD10", "#FC0007", "#8B2659", "#2A2A2A", "#118EFE", "#53D529", "#F5AD10", "#FC0007", "#8B2659", "#2A2A2A", "#118EFE", "#53D529", "#F5AD10", "#FC0007", "#8B2659", "#2A2A2A"}
    
set bJsonStr to array2DToJSONArray(bList) of me
    
    
set aString to NSString’s stringWithFormat_(myStr, aJsonStr, bJsonStr, cJsonStr) as string
    
    
set paramObj to {myMessage:dMes1, mySubMessage:dMes2, htmlStr:aString}
    
–my browseStrWebContents:paramObj–for debug
    
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true
  end displayPieChart
  
  
on browseStrWebContents:paramObj
    set aMainMes to myMessage of paramObj
    
set aSubMes to mySubMessage of paramObj
    
set htmlString to (htmlStr of paramObj)
    
    
set aWidth to 920
    
set aHeight to 500
    
    
–WebViewをつくる
    
set aConf to WKWebViewConfiguration’s alloc()’s init()
    
    
–指定HTML内のJavaScriptをFetch
    
set jsSource to pickUpFromToStr(htmlString, "<script>", "</script>") of me
    
    
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
    
set userContentController to WKUserContentController’s alloc()’s init()
    
userContentController’s addUserScript:(userScript)
    
aConf’s setUserContentController:userContentController
    
    
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) configuration:aConf
    
aWebView’s setNavigationDelegate:me
    
aWebView’s setUIDelegate:me
    
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
    
using terms from scripting additions
      set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
    end using terms from
    
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
    
    
— set up alert  
    
set theAlert to NSAlert’s alloc()’s init()
    
tell theAlert
      its setMessageText:aMainMes
      
its setInformativeText:aSubMes
      
its addButtonWithTitle:"OK"
      
–its addButtonWithTitle:"Cancel"
      
its setAccessoryView:aWebView
      
      
set myWindow to its |window|
    end tell
    
    
— show alert in modal loop
    
NSRunningApplication’s currentApplication()’s activateWithOptions:0
    
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
    
    
–Stop Web View Action
    
set bURL to |NSURL|’s URLWithString:"about:blank"
    
set bReq to NSURLRequest’s requestWithURL:bURL
    
aWebView’s loadRequest:bReq
    
    
if (my returnCode as number) = 1001 then error number -128
  end browseStrWebContents:
  
  
  
on doModal:aParam
    set (my returnCode) to (aParam’s runModal()) as number
  end doModal:
  
  
  
on viewDidLoad:aNotification
    return true
  end viewDidLoad:
  
  
  
on fetchJSSourceString(aURL)
    set jsURL to |NSURL|’s URLWithString:aURL
    
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
    
return jsSourceString
  end fetchJSSourceString
  
  
  
on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
    set a1Offset to offset of s1Str in aStr
    
if a1Offset = 0 then return false
    
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
    
set a2Offset to offset of s2Str in bStr
    
if a2Offset = 0 then return false
    
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
    
return cStr as string
  end pickUpFromToStr
  
  
  
on array2DToJSONArray(aList)
    set anArray to NSMutableArray’s arrayWithArray:aList
    
set jsonData to NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
    
set resString to NSString’s alloc()’s initWithData:jsonData encoding:(NSUTF8StringEncoding)
    
return resString
  end array2DToJSONArray
  
  
  
on parseByDelim(aData, aDelim)
    set curDelim to AppleScript’s text item delimiters
    
set AppleScript’s text item delimiters to aDelim
    
set dList to text items of aData
    
set AppleScript’s text item delimiters to curDelim
    
return dList
  end parseByDelim
  
  
on hasInternetConnection(aURL)
    set aURL to current application’s |NSURL|’s alloc()’s initWithString:aURL
    
set aReq to current application’s NSURLRequest’s alloc()’s initWithURL:aURL cachePolicy:(current application’s NSURLRequestReloadIgnoringLocalCacheData) timeoutInterval:5.0
    
set urlRes to (current application’s NSURLConnection’s sendSynchronousRequest:aReq returningResponse:(missing value) |error|:(missing value))
    
if urlRes = missing value then
      return false
    else
      return true
    end if
  end hasInternetConnection
end script

★Click Here to Open This Script 

Posted in dialog Internet JavaScript JSON URL | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSJSONSerialization NSMutableArray NSRunningApplication NSString NSURL NSURLRequest NSUTF8StringEncoding WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

アラートダイアログ上にd3-cloudを用いてワードクラウドを表示

Posted on 6月 14, 2020 by Takaaki Naganoya

d3-cloudを用いてワードクラウドを表示するAppleScriptです。

本AppleScriptでは、

 {|word|:"NSString", |count|:97}

のように、登場単語、登場回数のように集計ずみのデータをJSONに変換してワードクラウド表示させています。この(AppleScriptの予約語にかぶりまくりな)属性ラベルはd3-cloud側の仕様です。

以前に作った「Tag CloudっぽいRTFの作成」のデータを用いて(属性ラベルを置換して)表示させたものがこちら(↑)。

Tag/Word Cloud関連のJavaScriptについては前から気になって調べてはいたのですが、「ローカルにデータを持つスクリプトが多い」「d3-cloud自体をローカルにインストールさせる例が多い」(実行速度などの問題で?)という状況で、ありもののスクリプトをこのアラートダイアログ上にはりつけたWkWebViewの「箱庭環境」で追加スクリプトのインストールなしで動くように書き換えるのに少々手間取りました。

CDNにホスティングされているd3-cloudを呼び出すことで、ローカルにd3-cloudをインストールせずに実行していますが、将来的にこれが変更・廃止された場合には別のWeb上のどこかにホスティングされているd3-cloudを呼び出すことになります。

ローカルにあるテキストコンテンツ(CotEditorで編集中の文章とか、Pagesで作成中のワープロ文章とか、Keynoteで作成中のプレゼン資料の内容とか)を分析して頻出単語を取り出し、タグクラウドといいますかワードクラウドで表示して全体の見通しをつけるというScriptは作りたいと思っていました。

AppleScript名:アラートダイアログ上にd3-cloudを用いてワードクラウドを表示.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2020/06/13
—
–  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 "WebKit"
use scripting additions

property |NSURL| : a reference to current application’s |NSURL|
property NSAlert : a reference to current application’s NSAlert
property NSString : a reference to current application’s NSString
property NSButton : a reference to current application’s NSButton
property WKWebView : a reference to current application’s WKWebView
property WKUserScript : a reference to current application’s WKUserScript
property NSURLRequest : a reference to current application’s NSURLRequest
property NSMutableArray : a reference to current application’s NSMutableArray
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSRunningApplication : a reference to current application’s NSRunningApplication
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding
property WKUserContentController : a reference to current application’s WKUserContentController
property WKWebViewConfiguration : a reference to current application’s WKWebViewConfiguration
property WKUserScriptInjectionTimeAtDocumentEnd : a reference to current application’s WKUserScriptInjectionTimeAtDocumentEnd

property returnCode : 0

set aRecArray to {{|word|:"NSString", |count|:97}, {|word|:"NSURL", |count|:78}, {|word|:"Keynote", |count|:52}, {|word|:"NSMutableArray", |count|:51}, {|word|:"NSAlert", |count|:50}, {|word|:"NSRunningApplication", |count|:49}, {|word|:"NSArray", |count|:48}, {|word|:"CotEditor", |count|:44}, {|word|:"NSColor", |count|:44}, {|word|:"Finder", |count|:41}, {|word|:"NSImage", |count|:39}, {|word|:"Numbers", |count|:38}, {|word|:"NSPredicate", |count|:34}, {|word|:"NSView", |count|:32}, {|word|:"NSButton", |count|:28}, {|word|:"Safari", |count|:28}, {|word|:"NSScreen", |count|:27}, {|word|:"iTunes", |count|:25}, {|word|:"NSUTF8StringEncoding", |count|:24}, {|word|:"NSDictionary", |count|:22}, {|word|:"NSScrollView", |count|:21}, {|word|:"NSBitmapImageRep", |count|:20}, {|word|:"NSFileManager", |count|:20}, {|word|:"NSMutableDictionary", |count|:19}, {|word|:"NSURLRequest", |count|:18}, {|word|:"NSUUID", |count|:18}, {|word|:"NSWindow", |count|:17}, {|word|:"NSBezierPath", |count|:16}, {|word|:"NSFont", |count|:16}, {|word|:"WKWebView", |count|:16}, {|word|:"WKWebViewConfiguration", |count|:16}, {|word|:"NSWorkspace", |count|:15}, {|word|:"Pages", |count|:15}, {|word|:"Script Editor", |count|:15}, {|word|:"System Events", |count|:15}, {|word|:"WKUserContentController", |count|:15}, {|word|:"WKUserScript", |count|:15}, {|word|:"NSAlertSecondButtonReturn", |count|:14}, {|word|:"NSJSONSerialization", |count|:14}, {|word|:"NSSortDescriptor", |count|:14}}
set jsonStr to array2DToJSONArray(aRecArray) of me

–https://qiita.com/january108/items/5388799531c1ace8324e
set myStr to "<!DOCTYPE HTML>
<html lang=\"en\">
<head>
<meta charset=\"UTF-8\">
<title>D3 Word Clouds</title>
<meta name=\"author\" content=\"d4i\"/>
</head>
<body>
<script src=\"http://d3js.org/d3.v3.min.js\" charset=\"utf-8\"></script>
<script src=\"https://cdnjs.cloudflare.com/ajax/libs/d3-cloud/1.2.5/d3.layout.cloud.min.js\"></script>

<script>
  var data = %@;
var width = 600,
height = 400,
fill = d3.scale.category20(),
maxcount = d3.max(data, function(d){ return d.count; } ),
wordcount = data.map(function(d) { return {text: d.word, size: d.count / maxcount * 100}; });

d3.layout.cloud().size([width, height])
.words(wordcount)
.padding(5)
.rotate(function() { return ~~(Math.random() * 2) * 90; })
.font(\"Impact\")
.fontSize(function(d) { return d.size; })
.on(\"end\", draw)
.start();

function draw(words) {
d3.select(\"body\").append(\"svg\")
.attr({
\"width\": width,
\"height\": height
})
.append(\"g\")
.attr(\"transform\", \"translate(\" + [ width >> 1, height >> 1 ] + \")\")
.selectAll(\"text\")
.data(words)
.enter()
.append(\"text\")
.style({
\"font-size\": function(d) { return d.size + \"px\"; },
\"font-family\": \"Impact\",
\"fill\": function(d, i) { return fill(i); }
})
.attr({
\"text-anchor\": \"middle\",
\"transform\": function(d) { return \"translate(\" + [d.x, d.y] + \")rotate(\" + d.rotate + \")\"; }
})
.text(function(d) { return d.text; });
}
</script>
</body>
</html>"

set aString to current application’s NSString’s stringWithFormat_(myStr, jsonStr) as string

set paramObj to {myMessage:"D3 Test", mySubMessage:"This is a d3-cloud word cloud test", htmlStr:aString}
–my browseStrWebContents:paramObj–for debug
my performSelectorOnMainThread:"browseStrWebContents:" withObject:(paramObj) waitUntilDone:true

on browseStrWebContents:paramObj
  set aMainMes to myMessage of paramObj
  
set aSubMes to mySubMessage of paramObj
  
set htmlString to (htmlStr of paramObj)
  
  
set aWidth to 620
  
set aHeight to 420
  
  
–WebViewをつくる
  
set aConf to WKWebViewConfiguration’s alloc()’s init()
  
  
–指定HTML内のJavaScriptをFetch
  
set jsSource to pickUpFromToStr(htmlString, "<script>", "</script>") of me
  
  
set userScript to WKUserScript’s alloc()’s initWithSource:jsSource injectionTime:(WKUserScriptInjectionTimeAtDocumentEnd) forMainFrameOnly:true
  
set userContentController to WKUserContentController’s alloc()’s init()
  
userContentController’s addUserScript:(userScript)
  
aConf’s setUserContentController:userContentController
  
  
set aWebView to WKWebView’s alloc()’s initWithFrame:(current application’s NSMakeRect(0, 0, aWidth, aHeight)) configuration:aConf
  
aWebView’s setNavigationDelegate:me
  
aWebView’s setUIDelegate:me
  
aWebView’s setTranslatesAutoresizingMaskIntoConstraints:true
  
using terms from scripting additions
    set bURL to |NSURL|’s fileURLWithPath:(POSIX path of (path to me))
  end using terms from
  
aWebView’s loadHTMLString:htmlString baseURL:(bURL)
  
  
— set up alert  
  
set theAlert to NSAlert’s alloc()’s init()
  
tell theAlert
    its setMessageText:aMainMes
    
its setInformativeText:aSubMes
    
its addButtonWithTitle:"OK"
    
–its addButtonWithTitle:"Cancel"
    
its setAccessoryView:aWebView
    
    
set myWindow to its |window|
  end tell
  
  
— show alert in modal loop
  
NSRunningApplication’s currentApplication()’s activateWithOptions:0
  
my performSelectorOnMainThread:"doModal:" withObject:(theAlert) waitUntilDone:true
  
  
–Stop Web View Action
  
set bURL to |NSURL|’s URLWithString:"about:blank"
  
set bReq to NSURLRequest’s requestWithURL:bURL
  
aWebView’s loadRequest:bReq
  
  
if (my returnCode as number) = 1001 then error number -128
end browseStrWebContents:

on doModal:aParam
  set (my returnCode) to (aParam’s runModal()) as number
end doModal:

on viewDidLoad:aNotification
  return true
end viewDidLoad:

on fetchJSSourceString(aURL)
  set jsURL to |NSURL|’s URLWithString:aURL
  
set jsSourceString to NSString’s stringWithContentsOfURL:jsURL encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
return jsSourceString
end fetchJSSourceString

on pickUpFromToStr(aStr as string, s1Str as string, s2Str as string)
  set a1Offset to offset of s1Str in aStr
  
if a1Offset = 0 then return false
  
set bStr to text (a1Offset + (length of s1Str)) thru -1 of aStr
  
set a2Offset to offset of s2Str in bStr
  
if a2Offset = 0 then return false
  
set cStr to text 1 thru (a2Offset – (length of s2Str)) of bStr
  
return cStr as string
end pickUpFromToStr

–リストを任意のデリミタ付きでテキストに
on retArrowText(aList, aDelim)
  set aText to ""
  
set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set aText to aList as text
  
set AppleScript’s text item delimiters to curDelim
  
return aText
end retArrowText

on array2DToJSONArray(aList)
  set anArray to NSMutableArray’s arrayWithArray:aList
  
set jsonData to NSJSONSerialization’s dataWithJSONObject:anArray options:(0 as integer) |error|:(missing value) –0 is
  
set resString to current application’s NSString’s alloc()’s initWithData:jsonData encoding:(current application’s NSUTF8StringEncoding)
  
return resString
end array2DToJSONArray

on parseByDelim(aData, aDelim)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to aDelim
  
set dList to text items of aData
  
set AppleScript’s text item delimiters to curDelim
  
return dList
end parseByDelim

★Click Here to Open This Script 

Posted in dialog JSON Tag | Tagged 10.13savvy 10.14savvy 10.15savvy NSAlert NSButton NSJSONSerialization NSMutableArray NSRunningApplication NSString NSURL NSURLRequest NSUTF8StringEncoding WKUserContentController WKUserScript WKUserScriptInjectionTimeAtDocumentEnd WKWebView WKWebViewConfiguration | Leave a comment

Post navigation

  • Older posts

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

Google Search

Popular posts

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

Tags

10.11savvy (1102) 10.12savvy (1243) 10.13savvy (1392) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (207) 13.0savvy (173) 14.0savvy (123) 15.0savvy (100) 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 (72) Pages (53) 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年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