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

カテゴリー: Text

macOS 13でNSNotFoundバグふたたび

Posted on 12月 1, 2022 by Takaaki Naganoya

また、Appleがやらかしてくれたようです。あの会社はNSNotFoundの定義値を何回間違えたら気が済むんでしょうか。これで通算4回目ぐらいです。macOS 10.12で発生し10.13.1で修正された定義値のミスをまたやったわけで、失敗がまったく生かされていない。大したものです。

今回のバグは、文字列の検索処理を行っているときに発見しました。指定文字列から対象文字列の登場位置と長さをリストで返す処理を行っていたところ、NSRangeからlocationを取得したときに、これ以上見つからない場所まで来たときにNSNotFoundを返すことを期待されるわけですが、そこに前回(macOS 10.12…10.13)と同様のおかしな値を返してきました。

この(↓)AppleScriptをmacOS 13上で実行すると、エラーになります。

追記:macOS 13.1でもエラーになります。

AppleScript名:テキストのキーワード検索(結果をNSRangeのlistで返す).scptd
— Created 2017-08-09 by Takaaki Naganoya
— 2017 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
–http://piyocast.com/as/archives/4771

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

set aStr to "ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
"

set aRes to searchWordRanges(aStr, "ATGC") of me as list
–>  {​​​​​{​​​​​​​location:0, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:10, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:20, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:30, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:40, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:50, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:60, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:70, ​​​​​​​length:4​​​​​}​​​}

on searchWordRanges(aTargText as string, aSearchStr as string)
  set aStr to NSString’s stringWithString:aTargText
  
set bStr to NSString’s stringWithString:aSearchStr
  
  
set hitArray to NSMutableArray’s alloc()’s init()
  
set cNum to (aStr’s |length|()) as integer
  
  
set aRange to current application’s NSMakeRange(0, cNum)
  
  
repeat
    set detectedRange to aStr’s rangeOfString:bStr options:(NSLiteralSearch) range:aRange
    
set aLoc to detectedRange’s location
    
log aLoc
    
if (detectedRange’s location) is equal to (current application’s NSNotFound) then exit repeat
    
    
hitArray’s addObject:detectedRange
    
    
set aNum to (detectedRange’s location) as number
    
set bNum to (detectedRange’s |length|) as number
    
    
set aRange to current application’s NSMakeRange(aNum + bNum, cNum – (aNum + bNum))
  end repeat
  
  
return hitArray
end searchWordRanges

★Click Here to Open This Script 

症状も前回とまったく同じなので、対処も前回と同じです。Appleは定期的にNSNotFoundの定義値を間違えるトンでもない会社なので(計測された事実)、NSNotFoundまわりはAppleがバグをやらかすことを前提に書いておくべきなんでしょう。

AppleScript名:テキストのキーワード検索(結果をNSRangeのlistで返す)v2.scptd
— Created 2017-08-09 by Takaaki Naganoya
— Modified 2022-12-01 by Takaaki Naganoya
— 2022 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

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

set aStr to "ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
"

set aRes to searchWordRanges(aStr, "ATGC") of me as list
–>  {​​​​​{​​​​​​​location:0, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:10, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:20, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:30, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:40, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:50, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:60, ​​​​​​​length:4​​​​​}, ​​​​​{​​​​​​​location:70, ​​​​​​​length:4​​​​​}​​​}
–> {{location:0, |length|:4}, {location:10, |length|:4}, {location:20, |length|:4}, {location:30, |length|:4}, {location:40, |length|:4}, {location:50, |length|:4}, {location:60, |length|:4}, {location:70, |length|:4}}

on searchWordRanges(aTargText as string, aSearchStr as string)
  set aStr to NSString’s stringWithString:aTargText
  
set bStr to NSString’s stringWithString:aSearchStr
  
  
set hitArray to NSMutableArray’s alloc()’s init()
  
set cNum to (aStr’s |length|()) as integer
  
  
set aRange to current application’s NSMakeRange(0, cNum)
  
  
repeat
    set detectedRange to aStr’s rangeOfString:bStr options:(NSLiteralSearch) range:aRange
    
set aLoc to detectedRange’s location
    
    
–macOS 13でAppleが新たに作ったバグを回避
    
if (aLoc > 9.999999999E+9) or (aLoc = current application’s NSNotFound) then exit repeat
    
    
hitArray’s addObject:detectedRange
    
    
set aNum to (detectedRange’s location) as number
    
set bNum to (detectedRange’s |length|) as number
    
    
set aRange to current application’s NSMakeRange(aNum + bNum, cNum – (aNum + bNum))
  end repeat
  
  
return hitArray
end searchWordRanges

★Click Here to Open This Script 

Appleと協議した結果、そこは仕様として認識していないという話で、とくに仕様を変えていないという話で平行線をたどってしまいました。

結局、書き方を少々変えることで問題そのものを「なかったこと」にする方向で決着。納得はできませんが、macOS 10.11とか10.12の時代に書いたScriptでもあり、その実行環境は手元に残っていません(macOS 10.13は何台か存在しています。macOS 11のASのCocoa呼び出しバグの検証時に活躍)。

AppleScript名:テキストのキーワード検索(結果をNSRangeのlistで返す)v3.scptd
— Created 2017-08-09 by Takaaki Naganoya
— Modified 2022-08-29 by Takaaki Naganoya
— 2023 Piyomaru Software
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

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

set aStr to "ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
ATGC ACGT ATGC AGTC
"

set aRes to searchWordRanges(aStr, "ATGC") of me as list
–> {{location:0, |length|:4}, {location:10, |length|:4}, {location:20, |length|:4}, {location:30, |length|:4}, {location:40, |length|:4}, {location:50, |length|:4}, {location:60, |length|:4}, {location:70, |length|:4}}

on searchWordRanges(aTargText as string, aSearchStr as string)
  set aStr to NSString’s stringWithString:aTargText
  
set bStr to NSString’s stringWithString:aSearchStr
  
  
set hitArray to NSMutableArray’s alloc()’s init()
  
set cNum to (aStr’s |length|()) as integer
  
  
set aRange to current application’s NSMakeRange(0, cNum)
  
  
repeat
    set detectedRange to aStr’s rangeOfString:bStr options:(NSLiteralSearch) range:aRange
    
set aLen to (detectedRange’s |length|) as number
    
    
if aLen = 0 then exit repeat
    
    
hitArray’s addObject:detectedRange
    
    
set aNum to (detectedRange’s location) as number
    
set bNum to (detectedRange’s |length|) as number
    
    
set aRange to current application’s NSMakeRange(aNum + bNum, cNum – (aNum + bNum))
  end repeat
  
  
return hitArray
end searchWordRanges

★Click Here to Open This Script 

Posted in Bug list Text | Tagged 13.0savvy NSLiteralSearch NSMakeRange NSMutableArray NSNotFound NSRange NSString | Leave a comment

Keynote書類のdefault title item部分に入った改行コードを除去する

Posted on 10月 9, 2022 by Takaaki Naganoya

Keynote v12.1書類の現在のスライドにあるdefault title itemに入っている改行コード(?)を除去するAppleScriptです。

結論から言うと、これが改行コード(LFとかCRとか)ではなく、Unicode特殊文字の「LINE SEPARATOR」(U+2028)であることが、データをhexdumpしてわかりました(AppleScript書いてるだけなのに、hexdumpのお世話になることの多いこと、多いこと)。なので、文字コードをUTF-16(AppleScript側の文字コード)で指定して置換を行なっています。

# AppleScriptのネイティブ文字コードはUTF-16BEです

Keynote書類の各Slideに存在しているdefault title itemから文字列(object text)を取得し、改行コードを除去する処理を行います。

AppleScript名:default title item部分に入った改行コードを除去する.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/10/09
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

tell application "Keynote"
  tell front document
    tell current slide
      set jj to object text of default title item
      
      
–https://www.fileformat.info/info/unicode/char/2028/index.htm
      
–Unicode Character ’LINE SEPARATOR’ (U+2028)
      
–UTF-16 (decimal)  8,232
      
set kk to repCharByID(jj, 8232, "") of me
      
log kk
      
    end tell
  end tell
end tell

–文字置換
on repCharByID(origText as string, targCharID as number, repChar as string)
  set targChar to string id targCharID
  
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 repCharByID

★Click Here to Open This Script 

Posted in Text | Tagged 12.0savvy Keynote | Leave a comment

AS関連データの取り扱いを容易にする(はずの)privateDataTypeLib

Posted on 8月 22, 2022 by Takaaki Naganoya

AS関連データの取り扱いを簡単にすることを目的に書き出した、privateDatatypeLibです。

プログラムとデータを分離して、データ記述部分を外部ファイル(設定ファイルなど)に追い出したときに、どのようなデータかを表現するための道具として試作してみました。

macOS上のデータ記述子は、

などのさまざまなデータ識別方法が存在していますが、どれも(自分の)用途には合わなかったので、検討・試作をはじめてみました。Predicatesが一番近いものの、不十分なのでいろんなデータ型や用途に拡張。あくまで自分用なので「public」などの宣言はとくに不必要と考え、縮小して処理を行なっています。

とくに、ファイルパスの処理なんて定型処理しかしないのに、わざわざ何かの表現を行う必要があるのはナンセンスですし、日付関連も割と余計な記述が多いように感じています。

また、緯度/経度のデータや座標データなども、もう少しなんとかならないかと思っています。

AppleScript名:privateDataTypeLib.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/08/22
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aData to "100"
set dType to "@number"
set bData to "" –不要なケース多数。不要な場合にはヌル文字を指定
set mePath to path to me –実際には、呼び出し側で取得して指定。ライブラリ自体のパスを求めているのはテスト実行時のみ
set aRes to getParameterAndCalc(aData, dType, bData, mePath) of me

on getParameterAndCalc(aData, dType, bData, mePath)
  if dType = "@string" then
    return normalizeByNFC(aData) of me
    
  else if dType = "@number" then
    set tmpA to zenToHan(aData) of charConvKit of me –全角→半角変換
    
set a to detectOutNumStr(tmpA) of me –数字+関連・意外の文字を除外
    
return normalizeByNFC(a) of me
    
  else if dType = "@filepath.sub.foldername" then
    set aClass to class of aData –aliasだったらPOSIX pathに変換。file…はどうなんだか
    
if aClass = alias then set aData to POSIX path of aData
    
return aData & bData & "/"
    
  else if dType = "@file.comment" then
    set aStr to getFinderComment(POSIX path of mePath) of me
    
return normalizeByNFC(aStr) of me
    
  else if dType = "@file.name" then
    set aClass to class of aData
    
if aClass = alias then set aData to POSIX path of aData
    
set aStr to (current application’s NSString’s stringWithString:aData)’s lastPathComponent()’s stringByDeletingPathExtension()
    
return normalizeByNFC(aStr as string) of me
    
  else if dType = "@date.month" then
    set curDate to current date
    
set curMonth to month of curDate as number
    
return curMonth as string
    
  else
    return aData
  end if
end getParameterAndCalc

on normalizeByNFC(aStr)
  set aNSStr to current application’s NSString’s stringWithString:aStr
  
set aNFC to aNSStr’s precomposedStringWithCanonicalMapping()
  
return aNFC as string
end normalizeByNFC

–ANK文字列以外のものをそぎ落とす
on detectOutNumStr(testStr)
  set sList to characters of testStr
  
set aStr to ""
  
  
repeat with i in sList
    if detectOutNumChar(i) of me then
      set aStr to aStr & (i as string)
    end if
  end repeat
  
  
return aStr
end detectOutNumStr

on detectOutNumChar(testText)
  –Numeric + Special char
  
set ankChar to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".", "-", "+", "E", ","}
  
  
set _testChar to testText as Unicode text
  
  
ignoring case
    repeat with i in _testChar
      set j to contents of i
      
if j is not in ankChar then
        return false
      end if
    end repeat
  end ignoring
  
  
return true
end detectOutNumChar

–Finderコメントを取得
on getFinderComment(aPOSIX)
  set aURL to current application’s |NSURL|’s fileURLWithPath:aPOSIX
  
set aMetaInfo to current application’s 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

script charConvKit
  — Created 2017-09-06 by Shane Stanley
  
— Modified 2017-09-06 by Takaaki Naganoya
  
use AppleScript
  
use framework "Foundation"
  
property parent : AppleScript
  
  
property NSString : a reference to current application’s NSString
  
property NSStringTransformFullwidthToHalfwidth : a reference to current application’s NSStringTransformFullwidthToHalfwidth
  
property NSStringTransformHiraganaToKatakana : a reference to current application’s NSStringTransformHiraganaToKatakana
  
property NSStringTransformLatinToHiragana : a reference to current application’s NSStringTransformLatinToHiragana
  
property NSStringTransformLatinToKatakana : a reference to current application’s NSStringTransformLatinToKatakana
  
property NSStringTransformToUnicodeName : a reference to current application’s NSStringTransformToUnicodeName
  
property NSStringTransformToXMLHex : a reference to current application’s NSStringTransformToXMLHex
  
  
–半角→全角変換
  
on hanToZen(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:true) as string
  end hanToZen
  
  
–全角→半角変換
  
on zenToHan(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformFullwidthToHalfwidth) |reverse|:false) as string
  end zenToHan
  
  
–ひらがな→カタカナ変換
  
on hiraganaToKatakana(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformHiraganaToKatakana) |reverse|:false) as string
  end hiraganaToKatakana
  
  
–カタカナ→ひらがな変換
  
on katakanaToHiraganaTo(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformHiraganaToKatakana) |reverse|:true) as string
  end katakanaToHiraganaTo
  
  
–ローマ字→ひらがな変換
  
on alphabetToHiragana(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:false) as string
  end alphabetToHiragana
  
  
–ひらがな→ローマ字変換
  
on hiraganaToalphabet(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToHiragana) |reverse|:true) as string
  end hiraganaToalphabet
  
  
–ローマ字→カタカナ変換
  
on alphabetToKatakana(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToKatakana) |reverse|:false) as string
  end alphabetToKatakana
  
  
–カタカナ→ローマ字変換
  
on katakanaToAlphabet(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformLatinToKatakana) |reverse|:true) as string
  end katakanaToAlphabet
  
  
–文字→Unicode Name変換
  
on characterToUnicodeName(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToUnicodeName) |reverse|:false) as string
  end characterToUnicodeName
  
  
–Unicode Name→文字変換
  
on unicodeNameToCharacter(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToUnicodeName) |reverse|:true) as string
  end unicodeNameToCharacter
  
  
–文字→XML Hex変換
  
on stringToXMLHex(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToXMLHex) |reverse|:false) as string
  end stringToXMLHex
  
  
–XML Hex→文字変換
  
on xmlHexTostring(aStr)
    set aString to NSString’s stringWithString:aStr
    
return (aString’s stringByApplyingTransform:(NSStringTransformToXMLHex) |reverse|:true) as string
  end xmlHexTostring
end script

★Click Here to Open This Script 

Posted in Calendar file File path folder Library Number Text URL | Tagged 10.14savvy 10.15savvy 11.0savvy 12.0savvy | 1 Comment

カギカッコのペア検出+エラーチェック

Posted on 8月 22, 2022 by Takaaki Naganoya

短い日本語の文章で、カギカッコ(「」)のペアがそろっているか、順番がきちんとしているか、個数が合っているかなどを検出するテスト用のAppleScriptです。

何回も同じようなScriptを書いてきたような気がします。

AppleScript名:カギカッコのペア検出+エラーチェック.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/08/22
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

set aStr to "「モーニング娘。」を表示した。「藤岡弘。」を表示。「藤岡弘。」。「藤岡弘。」"
–set aStr to "「モーニング娘。」を表示した。「藤岡弘。」を表示「。「藤岡弘。」。」「藤岡弘。」"

set kagikakkoList to {"「", "」"} –カギカッコ 開始文字、終了文字ペア。英語で言うところのダブルクォート(「“」, 「”」)
set aRes to pairKagikakkoCheckAndReturnPositionPairts(aStr, kagikakkoList) of me
–> {{1, 9}, {16, 21}, {26, 31}, {33, 38}}–正常な場合
–> false –カッコの対応についてエラーがある場合

on pairKagikakkoCheckAndReturnPositionPairts(aStr as string, kagikakkoList as list)
  set aList to {}
  
  
–カギカッコの開始文字、終了文字の位置をシーケンシャルにピックアップする
  
repeat with i in kagikakkoList
    set j to contents of i
    
set aRes to scanStrMultiple(aStr, j) of me
    
set the end of aList to aRes
  end repeat
  
–> {{1, 16, 26, 33}, {9, 21, 31, 38}}
  
  
–カギカッコの個数が合っていないかどうかチェック
  
if length of aList is not equal to length of kagikakkoList then error "Separator number error"
  
  
  
–ペアリスト作成前に、カギカッコの開始、修了の文字の個数が合っているかをチェック
  
set startLen to length of first item of aList
  
set endLen to length of second item of aList
  
if startLen is not equal to endLen then error "Separator pair number is not same"
  
  
  
–ペアリストを作成
  
set pairList to {}
  
repeat with i from 1 to (length of first item of aList)
    set item1Dat to contents of item i of (first item of aList)
    
set item2Dat to contents of item i of (second item of aList)
    
set the end of pairList to {item1Dat, item2Dat}
  end repeat
  
–> {{1, 9}, {16, 21}, {25, 32}, {27, 34}, {35, 40}}
  
  
  
–ペアリストのクロスチェック
  
repeat with i from 1 to ((length of pairList) – 1)
    set {itemA1, itemA2} to contents of item i of pairList
    
set {itemB1, itemB2} to contents of item (i + 1) of pairList
    
    
if itemA1 > itemA2 then return false
    
if itemA1 > itemB1 then return false
    
if itemA2 > itemB1 then return false
  end repeat
  
  
return pairList
end pairKagikakkoCheckAndReturnPositionPairts

on scanStrMultiple(aStr, targStr)
  set aStrLen to length of aStr
  
set tLen to (length of targStr)
  
set posOffset to 0
  
  
copy aStr to bStr
  
set aList to {}
  
  
repeat
    set aRes to offset of targStr in bStr
    
if aRes = 0 then exit repeat
    
    
if aRes is not in aList then
      set the end of aList to (aRes + posOffset)
      
set posOffset to posOffset + aRes
    end if
    
    
if posOffset ≥ aStrLen then exit repeat
    
    
set tPos to (aRes + tLen)
    
if tPos > length of bStr then
      set tPos to length of bStr
    end if
    
    
if (length of bStr) ≤ tLen then exit repeat
    
    
set bStr to text tPos thru -1 of bStr
  end repeat
  
  
return aList
end scanStrMultiple

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

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

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

★Click Here to Open This Script 

Posted in Natural Language Processing Text | Tagged 10.15savvy 11.0savvy 12.0savvy | Leave a comment

Keynoteの表で選択中のセルの文字列長さを一覧表で表示

Posted on 7月 4, 2022 by Takaaki Naganoya

Keynoteの最前面の書類で表示中のスライド中の表で、選択中の各セルの内容の文字列の長さを表インタフェースで一覧表示するAppleScriptです。

こんなKeynote書類上の表があったとして、その選択中の各セルに入っている文字列の長さを、AppleScriptライブラリ「display table by list」を用いて一覧表示します。実行には同ライブラリのインストールが必要です。

実行の前提として、Keynoteで書類をオープンしてあり、書類上の表の集計対象のセルを選択しておくことが必要です。

文字列長と元の文字列を一覧表示します。

AppleScript名:Keynoteの表で選択中のセルの文字列長さを一覧表で表示.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/07/04
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5" — El Capitan (10.11) or later
use framework "Foundation"
use scripting additions
use easyTable : script "display table by list"

tell application "Keynote"
  tell front document
    tell current slide
      try
        set theTable to first table whose class of selection range is range
      on error
        display notification "Numbers: There is no selection"
        
return
      end try
      
      
tell theTable
        set aRes to value of cells of selection range
      end tell
    end tell
  end tell
end tell

set fLabels to {"文字列長", "文字列"}

set newList to {}
repeat with i in aRes
  set j to contents of i
  
if j is not equal to missing value then
    set aLen to length of j
    
set the end of newList to {aLen, j}
  end if
end repeat

tell current application
  set aRes to (display table by list newList main message "テキストの文字列長" sub message "Keynoteの表で選択中のテキストの各セルの文字列長" size {800, 400} labels fLabels icon "")
end tell

★Click Here to Open This Script 

Posted in list Text | Tagged 10.15savvy 11.0savvy 12.0savvy Keynote | 1 Comment

Keynoteの最前面の書類で表示中のスライドで選択中の表の指定行のテキストのセルの文字列長の分布を計算

Posted on 7月 4, 2022 by Takaaki Naganoya

Keynoteの最前面の書類で表示中のスライド(ページ)で選択中の表の、指定行のテキストのセルの文字列長の分布をグラフ出力するAppleScriptです。

# Blogの英訳本の企画を動かしていますが、たしかに日本語のタイトルを(日本人が)見てもけっこうわかりにくいのかも?
# タイトルをGoogle翻訳に突っ込むと、それなりに「そんな感じじゃね?」という英語が出てきますが、、、

こういう、電子書籍の紹介文を掲載しているページがあって、そこに書いてある紹介文の長さの分布を計算してみるために、ありものの部品を組み合わせて作ったものです。

「だいたい3行ぐらい」というふんわりとしたルールのもとに18冊分ほど書いてきましたが、本の冊数が増えてページを追加するにあたって、文字列長の分布を計算してみようと思い立ってScriptを書いてみました。

043 #
044 ##
045 
046 ###
047 
048 ##
049 ###
050 #
051 #
052 #
053 
054 #
055 
056 #
057 #
058 
059 
060 
061 
062 
063 
064 
065 
066 
067 
068 #

分布はわかりましたが、似たような処理を行うことが多いので、何か簡単なGUIをつけておいたほうがいいような気がするような、、、

AppleScript名:選択中の表のデータを取得して、特定の行のデータの文字列長を集計.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/07/04
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

property NSMutableArray : a reference to current application’s NSMutableArray

set aList to get2DListFromKeynoteTableSelection() of me
if aList = "" then return false

–取得した各セルの文字列長をすべて取得する
set aLen to length of aList
set aaLen to length of item 1 of aList

set nList to {}
repeat with i from 3 to (aLen) by 3
  repeat with ii from 1 to aaLen
    set the end of nList to length of (contents of item ii of item i of aList)
  end repeat
end repeat

–文字列長の度数分布を計算
set minRes to calcMin(nList) of me
set maxRes to calcMax(nList) of me

set itemCount to maxRes – minRes
set n2List to makeBlankListByItem(itemCount + 1, 0) of me

repeat with i in nList
  set j to contents of i
  
set tmpNum to contents of item (j – minRes + 1) of n2List
  
set tmpNum to tmpNum + 1
  
set item (j – minRes + 1) of n2List to tmpNum
end repeat

–文字列長の度数分布のグラフを文字出力
set tList to {}
set aCount to minRes

repeat with i in n2List
  set gText to returnRepeatedText(i, "#") of me
  
set tNumStr to makeFN(aCount, 3) of me
  
set totalText to tNumStr & " " & gText
  
set the end of tList to totalText
  
set aCount to aCount + 1
end repeat

return retArrowText(tList, return) of me

–リストを任意のデリミタ付きでテキストに
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 returnRepeatedText(aNum, aChar)
  set aText to ""
  
repeat aNum times
    set aText to aText & aChar
  end repeat
  
return aText
end returnRepeatedText

on makeBlankListByItem(itemNum, blankItem)
  set tmpList to {}
  
repeat itemNum times
    set the end of tmpList to blankItem
  end repeat
  
return tmpList
end makeBlankListByItem

on calcMin(aList as list)
  set tmpFItem to first item of aList
  
set aClass to class of tmpFItem
  
  
set nArray to (NSMutableArray’s arrayWithArray:aList)
  
if aClass = real then
    set maxRes to (nArray’s valueForKeyPath:"@min.self")’s doubeValue()
  else
    set maxRes to (nArray’s valueForKeyPath:"@min.self")’s intValue()
  end if
  
  
return maxRes
end calcMin

on calcMax(aList as list)
  set tmpFItem to first item of aList
  
set aClass to class of tmpFItem
  
  
set nArray to (NSMutableArray’s arrayWithArray:aList)
  
if aClass = real then
    set maxRes to (nArray’s valueForKeyPath:"@max.self")’s doubeValue()
  else
    set maxRes to (nArray’s valueForKeyPath:"@max.self")’s intValue()
  end if
  
  
return maxRes
end calcMax

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

–Keynoteで選択中の表のすべてのセルのデータを2Dで返す
on get2DListFromKeynoteTableSelection()
  tell application "Keynote"
    tell front document
      tell current slide
        try
          set theTable to first table whose class of selection range is range
        on error
          return "" –何も選択されてなかった場合
        end try
        
        
tell theTable
          set selList to value of every cell –すべてののデータを取得
          
set selHeight to count every row
          
set selWidth to count every column
          
        end tell
      end tell
    end tell
  end tell
  
  
set aLen to length of selList
  
set aaLen to selHeight
  
  
set bList to {}
  
repeat with i from 1 to aaLen
    set aHoriList to {}
    
    
repeat with ii from 1 to selWidth
      set j1 to ii + (i – 1) * selWidth
      
set tmpCon to contents of item j1 of selList
      
      
set aClass to class of tmpCon
      
if aClass = number or aClass = integer or aClass = real then
        set tmpCon to tmpCon as integer
      end if
      
      
set the end of aHoriList to tmpCon
    end repeat
    
    
set the end of bList to aHoriList
  end repeat
  
  
return bList
end get2DListFromKeynoteTableSelection

–テキストを指定デリミタでリスト化
on parseByDelim(aData, aDelim)
  set aText to current application’s NSString’s stringWithString:aData
  
set aList to aText’s componentsSeparatedByString:aDelim
  
return aList as list
end parseByDelim

–1D/2D Listのユニーク化
on uniquifyList(aList as list)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set bArray to aArray’s valueForKeyPath:"@distinctUnionOfObjects.self"
  
return bArray as list
end uniquifyList

★Click Here to Open This Script 

Posted in Text | Tagged 10.15savvy 11.0savvy 12.0savvy Keynote | Leave a comment

相対パスを計算で求める

Posted on 6月 14, 2022 by Takaaki Naganoya

指定のPOSIX pathを元に相対パスを計算するAppleScriptです。

Unix shellであれば、相対パスについては「../../../」などと表現できるわけですが、シェルを介さずに計算する方法が見つからなかったので、必要に迫られて作っておきました。

AppleScript名:上位パスを相対的に求める.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/06/14
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

set aFile to "/Users/me/Documents/1/2/3"

set aNum to 1
set bFile to getUpperDir(aFile, aNum) of me

–> "/Users/me/Documents/1/2"

set aNum to 2
set cFile to getUpperDir(aFile, aNum) of me
–>"/Users/me/Documents/1"

set aNum to 3
set dFile to getUpperDir(aFile, aNum) of me
–> "/Users/me/Documents"

–与えられたパスから指定階層「上」のパスを求める
on getUpperDir(aPOSIXpath as string, aNum as integer)
  if aNum < 1 then return aPOSIXpath
  
set pathString to current application’s NSString’s stringWithString:aPOSIXpath
  
repeat aNum times
    set pathString to pathString’s stringByDeletingLastPathComponent()
  end repeat
  
return pathString as string
end getUpperDir

★Click Here to Open This Script 

AppleScript名:下位パスを配列で追加して求める.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/06/14
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

set aPOSIXpath to "/Users/me/Documents/" –AppleScriptのPOSIX path表現(フォルダを表現するさい、末尾に「/」)
set catDirList to {"1", "2", "3"}
set bPath to getChildPathByAppendingDirsByArray(aPOSIXpath, catDirList) of me
–> /Users/me/Documents/1/2/3

set aPOSIXpath to "/Users/me/Documents" –CocoaのPOSIX path表現(フォルダを表現する場合でも、末尾に「/」入らず)
set catDirList to {"3", "2", "1"}
set cPath to getChildPathByAppendingDirsByArray(aPOSIXpath, catDirList) of me
–> "/Users/me/Documents/3/2/1"

on getChildPathByAppendingDirsByArray(aPOSIXpath, catDirList)
  set pathString to current application’s NSString’s stringWithString:aPOSIXpath
  
set aList to (pathString’s pathComponents()) as list
  
set aList to aList & catDirList
  
  
set bPath to current application’s NSString’s pathWithComponents:aList
  
return bPath as string
end getChildPathByAppendingDirsByArray

★Click Here to Open This Script 

Posted in File path list Text | Tagged 10.13savvy 10.14savvy 10.15savvy 11.0savvy 12.0savvy | Leave a comment

Safariで表示中のYouTubeムービーのサムネイル画像を取得

Posted on 5月 9, 2022 by Takaaki Naganoya

Safariで表示中のYouTubeムービーのサムネール画像を取得、保存、表示するAppleScriptです。

YouTubeのムービーのサムネール画像の取得方法を確認し、動作確認用にダイアログ表示+画像保存の機能を追加したものです。Script Debugger上で動かしている分には、NSImageの内容を結果表示ビューワで自動表示されますが、ない人向けに付けた機能です。

画像自体は、「ピクチャ」フォルダにUUIDつきでPNG形式で保存します。

–> Download Script bundle with Library

掲載リストには、画像表示ライブラリが含まれていないため、そのままでは実行できません。上記のScript Bundleをダウンロードして実行する必要があります。

AppleScript名:Safariで表示中のYouTubeムービーのサムネイル画像を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/05/09
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"
use imgLib : script "imageDisplayLib"

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

tell application "Safari"
  tell front document
    try
      set aURL to URL
    on error
      set aURL to "https://www.youtube.com/watch?v=_fmDtIV9vvI"
    end try
  end tell
end tell

if aURL does not start with "https://www.youtube.com/watch?" then return

set urlDict to parseURLParamsAsDict(aURL) of me
set aParam to urlDict’s valueForKey:"v"
if aParam = missing value then return

set imgURL to "https://i1.ytimg.com/vi/" & (aParam as string) & "/mqdefault.jpg"
set newURL to |NSURL|’s URLWithString:imgURL
set aImg to NSImage’s alloc()’s initWithContentsOfURL:newURL

set imgPath to (POSIX path of (path to pictures folder) & ((aParam as string) & "_") & (NSUUID’s UUID()’s UUIDString()) as string) & ".png"

–Save
saveNSImageAtPathAsPNG(aImg, imgPath) of me

–Display
dispImage(aImg, "YouTube thumbnail") of imgLib

on parseURLParamsAsDict(aURL)
  set components to NSURLComponents’s alloc()’s initWithString:aURL
  
set qList to (components’s query())’s componentsSeparatedByString:"&"
  
  
set paramRec to NSMutableDictionary’s dictionary()
  
  
repeat with i in qList
    set keyAndValues to (i’s componentsSeparatedByString:"=")
    (
paramRec’s setObject:(keyAndValues’s objectAtIndex:1) forKey:(keyAndValues’s objectAtIndex:0))
  end repeat
  
  
return paramRec
end parseURLParamsAsDict

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

★Click Here to Open This Script 

Posted in Image Record Text URL | Tagged 10.14savvy 10.15savvy 11.0savvy 12.0savvy Safari | Leave a comment

Keynoteのタイトル内の強制改行コードを置換….できない?

Posted on 5月 3, 2022 by Takaaki Naganoya

Keynote v12のスライド(ページ)内のタイトル(default title item)内のテキスト(object text)の強制改行を置換(削除)できないという件についての試行錯誤です。

もともと、v12以前のバージョンのKeynoteでは、LF+LFを削除することで、タイトル内のテキストの改行削除は行えていました。

ただ、同様の処理では強制改行を削除し切れないようで….

--> {"AppleSript+NSImageでよく使う

基礎的な処理一覧", "画像ファイル
変換処理", "画像回転処理", "画像ファイルからの

情報取得処理", "画像リサイズ処理", "画像フィルタ処理"}

いまひとつすっきりしません。

Keynoteのタイトル文字列をコピーして、CotEditor(文字コードをAppleScriptと同じUTF16BEに変更)の書類上にペーストすると0Ahと表示するのですが、どうもKeynote上では異なるようで困ります。

AppleScript名:Keynote v12上の選択中のスライドのタイトルをテキスト化(改行削除)(未遂).scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/05/03
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"

set repTargList to {string id 10 & string id 10, string id 10, string id 11, string id 13} –LF,CR,VTab一括置換

tell application "Keynote"
  tell front document
    set aSel to selection
    
set aList to {}
    
    
repeat with i in aSel
      set tmpClass to class of i
      
if tmpClass = slide then
        set tmpStr0 to object text of default title item of i
        
set hexList to retHexDumpedStr(tmpStr0) of me
        
set tmpStr1 to (paragraphs of tmpStr0) –パラグラフごとにlistにしてみた
        
log tmpStr1
        
set tmpStr2 to replaceTextMultiple(tmpStr1 as string, repTargList, "") of me as string
        
set the end of aList to tmpStr2
      end if
    end repeat
  end tell
end tell

return aList

–文字列の前後の改行と空白文字を除去
on cleanUpText(someText)
  set theString to current application’s NSString’s stringWithString:someText
  
set theString to theString’s stringByReplacingOccurrencesOfString:" +" withString:" " options:(current application’s NSRegularExpressionSearch) range:{location:0, |length|:length of someText}
  
set theWhiteSet to current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet()
  
set theString to theString’s stringByTrimmingCharactersInSet:theWhiteSet
  
return theString as text
end cleanUpText

–指定文字列からCRLFを除去
on cleanUpCRLF(aStr)
  set aString to current application’s NSString’s stringWithString:aStr
  
set bString to aString’s stringByReplacingOccurrencesOfString:(string id 10) withString:"" –remove LF
  
set cString to bString’s stringByReplacingOccurrencesOfString:(string id 13) withString:"" –remove CR
  
set dString to cString as string
  
return dString
end cleanUpCRLF

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

–任意のデータから特定の文字列を複数パターン一括置換
on replaceTextMultiple(origData as string, origTexts as list, repText as string)
  set curDelim to AppleScript’s text item delimiters
  
set AppleScript’s text item delimiters to origTexts
  
set origData to text items of origData
  
set AppleScript’s text item delimiters to {repText}
  
set origData to origData as text
  
set AppleScript’s text item delimiters to curDelim
  
return origData
end replaceTextMultiple

on hexDumpString(aStr as string)
  set theNSString to current application’s NSString’s stringWithString:aStr
  
set theNSData to theNSString’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set theString to (theNSData’s |debugDescription|()’s uppercaseString())
  
  
–Remove "<" ">" characters in head and tail
  
set tLength to (theString’s |length|()) – 2
  
set aRange to current application’s NSMakeRange(1, tLength)
  
set theString2 to theString’s substringWithRange:aRange
  
  
–Replace Space Characters
  
set aString to current application’s NSString’s stringWithString:theString2
  
set bString to aString’s stringByReplacingOccurrencesOfString:" " withString:""
  
  
set aResList to splitString(bString, 2)
  
–> {​​​​​"E3", ​​​​​"81", ​​​​​"82", ​​​​​"E3", ​​​​​"81", ​​​​​"84", ​​​​​"E3", ​​​​​"81", ​​​​​"86", ​​​​​"E3", ​​​​​"81", ​​​​​"88", ​​​​​"E3", ​​​​​"81", ​​​​​"8A"​​​}
  
  
return aResList
end hexDumpString

–Split NSString in specified aNum characters
on splitString(aText, aNum)
  set aStr to current application’s NSString’s stringWithString:aText
  
if aStr’s |length|() ≤ aNum then return aText
  
  
set anArray to current application’s NSMutableArray’s new()
  
set mStr to current application’s NSMutableString’s stringWithString:aStr
  
  
set aRange to current application’s NSMakeRange(0, aNum)
  
  
repeat while (mStr’s |length|()) > 0
    if (mStr’s |length|()) < aNum then
      anArray’s addObject:(current application’s NSString’s stringWithString:mStr)
      
mStr’s deleteCharactersInRange:(current application’s NSMakeRange(0, mStr’s |length|()))
    else
      anArray’s addObject:(mStr’s substringWithRange:aRange)
      
mStr’s deleteCharactersInRange:aRange
    end if
  end repeat
  
  
return (current application’s NSArray’s arrayWithArray:anArray) as list
end splitString

–与えられたデータ内容をhexdumpして返す
on retHexDumpedStr(aStr)
  set aRes to do shell script "echo " & quoted form of aStr & " | hexdump -v "
  
return aRes
end retHexDumpedStr

★Click Here to Open This Script 

Posted in Object control Text | Tagged 10.15savvy 11.0savvy 12.0savvy Keynote | Leave a comment

NSCharacterSetの使い方を間違えた

Posted on 3月 30, 2022 by Takaaki Naganoya

これまでに、NSMutableCharacterSetを使うべきところにNSCharacterSetを指定していたAppleScriptがあって、これは自分のミスだったのですが……これまで、この書き方で動いてしまって気が付かなかったものでした(Thanks Shane!)

このところ、AppleScriptの処理系やmacOS自体のバグが続いたので、不可思議な問題を見つけると「これどーなの?」と関係筋に確認をとっています。Appleにレポートする前に複数のScripterに確認をとって、「本当にそうなのか?」「再現性のあるトラブルか?」という話をしています。そんな中で、これは自分のミスでした。

on chkSymbol:checkString
	set muCharSet to NSCharacterSet's alloc()'s init()
	muCharSet's addCharactersInString:"$\"!~&=#[]._-+`|{}<>?%^*/'@-/:;(),"
	set ret to my chkCompareString:checkString baseString:muCharSet
	return ret as boolean
end chkSymbol:

それが、macOS 12.3で急に厳密に動かなくなり….逆にいえば、「なんでこれまでは動いていたんだろう?」というところではあるのですが、割と文字種別の判定はいろんなScriptで行なっているので影響が大きいところです。

on chkSymbol:checkString
	set muCharSet to NSMutableCharacterSet's alloc()'s init()
	muCharSet's addCharactersInString:"$\"!~&=#[]._-+`|{}<>?%^*/'@-/:;(),"
	set ret to my chkCompareString:checkString baseString:muCharSet
	return ret as boolean
end chkSymbol:

Cocoa Scripting Course #1 NSStringの付録サンプルScriptに該当するものがあるため、バグ情報を掲載しています。
また、差し替えたものをダウンロードできるように購入ページ上のアーカイブを更新いたします。

Posted in Text | Tagged 12.0savvy NSCharacterSet NSMutableCharacterSet | 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

書籍フォルダの階層をさかのぼって、ツメに掲載する最大チャプターを推測 v2

Posted on 3月 4, 2022 by Takaaki Naganoya

電子書籍を作るのにPagesやKeynoteを使っており、「AppleScriptによるWebブラウザ自動操縦ガイド」(以下、Webブラウザガイド)も全ページPagesで作っています。

PagesやKeynoteでは書籍作成用としては機能が素朴すぎて、足りない点はAppleScriptでツールを作って、作業の手間を減らしています。それらの補助Scriptは、各種パラメータをその本に合わせて固定して使用しています。

Webブラウザガイドは全14章で構成されているため、ページの左右につけている「ツメ」(Index)は1から14までの数字が入っています。

今後もツメチェックAppleScript(座標、塗りつぶし色と非選択色の自動判別、ファイル名からの該当章の自動ピックアップ)を他の書籍用にも運用していくつもりですが、この「全14章」という仕様は固定なので、章構成が異なる他の本のプロジェクトでは、自動で章の数をかぞえてくれるとよさそうだと考えました。

だいたい電子書籍のファイルについては、フォルダ分けして2階層ぐらいで管理しているので、その階層数については決め打ちでDoc rootフォルダを計算(parent of parent of….)するようにしました。そして、全フォルダのフォルダ名称を取得。

ダイアログで最終章を選択させると、そこから章番号を自動抽出して(「XX章」と書かれていることが前提)、その番号を返します。

こういう用途を考えると、階層構造をそのまま選択できるNSOutlineViewを選択用の部品に使えると便利で……これまでにもedama2さんと意見交換しつつNSOutlineViewをNSAlertダイアログ上に表示するといった試作も何回か検討してきたのですが、スクリプトエディタ/Script Debugger上で記述するAppleScriptObjCではこの部品を扱うのがとても難しいんですね。

ならば、Xcode上で記述するAppleScriptObjCにAppleScript用語辞書を持たせて、階層ファイル構造を選択させる専用の補助アプリケーションを作ってもいいのかも? ただ、Xcode 13.x系が壊れて使えないままの環境であるため、いまXcodeでビルドするわけにもいかないのでした。

choose fileコマンドやchoose folderコマンドに「icon view」「list view」「column view」といった初期表示状態を指定できる機能があれば、それで済むような気もしますが、どうせAppleに要望出してもこういうのは通らないので、自分で作ったほうが確実で早いですわー。

にしても、この通常ウィンドウと見分けがつかないファイル選択ダイアログ、macOS 11で最初に見たときには「正気か?!」と、腰を抜かしました。あいかわらず、この決定を下した責任者は●●だと思いますが、せめてもう少し視覚的に見分けがつくようにできなかったもんでしょうか。

AppleScript名:書籍フォルダの階層をさかのぼって、ツメに掲載する最大チャプターを推測 v2.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/02/26
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

tell application "Pages"
  tell front document
    set filePath to (file of it) as alias
  end tell
end tell

tell application "Finder"
  set parentFol to (parent of parent of filePath)
  
tell parentFol
    set fNames to name of every folder
  end tell
end tell

set folName to contents of (choose from list fNames with prompt "書籍のツメに載せる最終章のフォルダを選択")
set cNum to retChapter(folName as string) of me
–> 14

–ファイル名から「章」情報を抽出
on retChapter(aStr)
  set wList to words of aStr
  
set aCount to 1
  
repeat with ii in wList
    set jj to contents of ii
    
if jj = "章" then
      return contents of item (aCount – 1) of wList
    end if
    
set aCount to aCount + 1
  end repeat
  
return 0 –Illeagal file name
end retChapter

★Click Here to Open This Script 

Posted in Books file File path Text | Tagged 12.0savvy Finder Pages | 1 Comment

CotEditor v4.1.2でAppleScript系の機能を追加

Posted on 2月 22, 2022 by Takaaki Naganoya

オープンソース開発されているフリーのテキストエディタ「CotEditor」v4.1.2において、AppleScript系の機能が追加されています。

・DocumentオブジェクトのhasBOM属性

has BOM (boolean, r/o) : Is the file encoding of the document has BOM (byte order mark)?

・convertコマンドのBOMオプション

convert v : Convert the document text to new encoding.
convert document : The document to convert encoding.
[lossy boolean] : Allows lossy conversion?
[BOM boolean] : Has the new encoding a BOM (byte order mark)?
to text : The new encoding, either in localized encoding name or an IANA charset name.
→ boolean : Did the convertion succeed?

・新設のjumpコマンド

jump v : Move the caret to the specified location. At least, either one of a parameter is required.
jump document : The document to move.
to line integer : The number of the line to go. If a negative value is provided, the line is counted from the end of the document.
[column integer] : The location in the line to jump. If a negative value is provided, the column is counted from the end of the line.

こんなサンプル書類があったとして、

AppleScriptのdocumentオブジェクトの文字データを取得してダンプしてみても、

--No BOM
{"E3", "81", "B4", "E3", "82", "88", "E3", "81", "BE", "E3", "82", "8B", "E3", "82", "BD", "E3", "83", "95", "E3", "83", "88", "E3", "82", "A6", "E3", "82", "A7", "E3", "82", "A2", "0A", "61", "62", "63", "64", "E9", "AB", "98", "E5", "B3", "B6", "E5", "B1", "8B", "65", "66", "67", "68", "69", "0A", "0A"}

--with BOM
{"E3", "81", "B4", "E3", "82", "88", "E3", "81", "BE", "E3", "82", "8B", "E3", "82", "BD", "E3", "83", "95", "E3", "83", "88", "E3", "82", "A6", "E3", "82", "A7", "E3", "82", "A2", "0A", "61", "62", "63", "64", "E9", "AB", "98", "E5", "B3", "B6", "E5", "B1", "8B", "65", "66", "67", "68", "69", "0A", "0A"}

この状態ではhasBOM属性値で差があっても、内部データでは差が出ません。これをファイルに書き込んで、ファイル内容についてチェックを行うと、

--No BOM
0000000 81e3 e3b4 8882 81e3 e3be 8b82 82e3 e3bd
0000010 9583 83e3 e388 a682 82e3 e3a7 a282 610a
0000020 6362 e964 98ab b3e5 e5b6 8bb1 6665 6867
0000030 0a69 000a                              
0000033

--With BOM
0000000 bbef e3bf b481 82e3 e388 be81 82e3 e38b
0000010 bd82 83e3 e395 8883 82e3 e3a6 a782 82e3
0000020 0aa2 6261 6463 abe9 e598 b6b3 b1e5 658b
0000030 6766 6968 0a0a                         
0000036

のように、差を検出できます。

Posted in news Object control Text | Tagged 10.15savvy 11.0savvy 12.0savvy CotEditor | Leave a comment

リストに入れたテキストで、冒頭に入ったマルつき数字をリナンバーする

Posted on 2月 12, 2022 by Takaaki Naganoya

日本語環境限定かもしれませんが、マルつき数字(①②③④⑤⑥⑦⑧⑨….)をさまざまな場所でよく使います。

データにマルつき数字を入れると、順番がわかりやすくてよいのですが、データそのものを入れ替えたときに番号をふり直すという手間がかかってしまいます。これがけっこうな手間になっています(地味に大変)。

そこで、

 {"③AAAAAA", "②BBBBB", "①CCCCC", "⑬DDDDDD", "⑫EEEE", "④FFFF", "⑤GGGG", "⑥HHHH", "⑲IIIII", "⑧JJJJ"}

というデータを本Scriptによって、

{"①AAAAAA", "②BBBBB", "③CCCCC", "④DDDDDD", "⑤EEEE", "⑥FFFF", "⑦GGGG", "⑧HHHH", "⑨IIIII", "⑩JJJJ"}

と、リナンバー処理します。

本処理は、絵文字の削除Scriptの副産物として生まれたもので、マル文字を削除したのちに番号をふり直して付加したところ、たいへん有用性を感じられました。

Keynote、Numbers、Pages…と、同じことができるようにScriptを整備し、CotEditor用にあったほうが便利だろうと考えて、CotEditor用にかきかえた際に、

–> Watch Demo Movie

「ほかのアプリケーションでも使えたほうが便利なので、再利用しやすいように部品化しておこう」

と、Script文でラッピングしてみたものがこれです。

AppleScript名:リストに入れたテキストで、冒頭に入ったマルつき数字をリナンバーする.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/02/12
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

set aOffset to 0

set aSelList to {"③AAAAAA", "②BBBBB", "①CCCCC", "⑬DDDDDD", "⑫EEEE", "④FFFF", "⑤GGGG", "⑥HHHH", "⑲IIIII", "⑧JJJJ"}

—データ中に丸つき数字が存在した場合には、最小のものを取得
set aOffset to (getMinimumNumFromNumberWithSign(aSelList) of maruNumKit)

–ユーザーに対して「本当に初期値がこれでいいのか?」をダイアログなどで確認したほうがいい

–Keynoteの表のセルから取得したデータから丸つき数字を除去する
set cList to removeNumberWithSignFromList(aSelList) of maruNumKit

–list中の各アイテムの冒頭に順次丸つき数字を追加する
set dList to {}
set aCount to 0

repeat with i in cList
  set j to convNumToNumWithSign(aCount + aOffset) of maruNumKit
  
set jj to contents of i
  
  
set the end of dList to (j & jj)
  
  
set aCount to aCount + 1
end repeat

return dList
–> {"①AAAAAA", "②BBBBB", "③CCCCC", "④DDDDDD", "⑤EEEE", "⑥FFFF", "⑦GGGG", "⑧HHHH", "⑨IIIII", "⑩JJJJ"}

–1D Arrayを改行コードをデリミタに指定しつつテキスト化
–set outStr to retDelimedText(dList, return) of maruNumKit

–丸つき数字を扱うキット
script maruNumKit
  
  
use AppleScript
  
use framework "Foundation"
  
use scripting additions
  
property parent : AppleScript
  
  
–1~50の範囲の数値を丸つき数字に変換して返す
  
on convNumToNumWithSign(aNum as number)
    if (aNum ≤ 0) or (aNum > 50) then return ""
    
set aStr to "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿"
    
set bChar to character aNum of aStr
    
return bChar
  end convNumToNumWithSign
  
  
  
–1D List上で指定データを検索してヒットしたアイテム番号を返す
  
on search1DList(aList, aTarg)
    set anArray to current application’s NSMutableArray’s arrayWithArray:aList
    
set anIndex to anArray’s indexOfObject:aTarg
    
if (anIndex = current application’s NSNotFound) or (anIndex > 9.99999999E+8) then
      return false
    end if
    
return (anIndex as integer) + 1 –convert index base (0 based to 1 based)
  end search1DList
  
  
  
–1D listのクリーニング
  
on cleanUp1DList(aList as list, cleanUpItems as list)
    set bList to {}
    
repeat with i in aList
      set j to contents of i
      
if j is not in cleanUpItems then
        set the end of bList to j
      else
        set the end of bList to ""
      end if
    end repeat
    
return bList
  end cleanUp1DList
  
  
  
–text in listから丸つき数字を除去する
  
on removeNumberWithSignFromList(aList as list)
    set bList to {}
    
repeat with i in aList
      set j to contents of i
      
set j2 to removeNumberWithSign(j) of me
      
set the end of bList to j2
    end repeat
    
return bList
  end removeNumberWithSignFromList
  
  
  
–文字列から丸つき数字を除去する
  
on removeNumberWithSign(aStr as text)
    set aNSString to current application’s NSString’s stringWithString:aStr
    
return (aNSString’s stringByReplacingOccurrencesOfString:"[\\U000024EA-\\U000024EA\\U00002460-\\U00002473\\U00003251-\\U000032BF\\U000024FF-\\U000024FF\\U00002776-\\U0000277F\\U000024EB-\\U000024F4\\U00002780-\\U00002789\\U0000278A-\\U00002793\\U000024F5-\\U000024FE]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, aNSString’s |length|()}) as text
  end removeNumberWithSign
  
  
  
–1D Listに入っているテキストから丸つき数字を抽出して数値化し、最小のものを求める
  
on getMinimumNumFromNumberWithSign(aList)
    set nList to {}
    
    
repeat with i in aList
      set j to contents of i
      
–与えられたテキストのうち、丸つき数字(白)の
      
set j2 to holdNumberWithSignOnly(j) of me
      
set n2List to characters of j2 –複数の丸つき数字が入っている場合に対処
      
      
repeat with ii in n2List
        set jj to contents of ii
        
set tmpNum to decodeNumFromNumWithSign(jj) of me
        
set the end of nList to tmpNum
      end repeat
      
    end repeat
    
    
set anArray to current application’s NSArray’s arrayWithArray:nList
    
set cRes to (anArray’s valueForKeyPath:"@min.self") as integer
    
return cRes
  end getMinimumNumFromNumberWithSign
  
  
  
–指定文字列から丸つき数字のみ抽出する
  
on holdNumberWithSignOnly(aStr as text)
    set aNSString to current application’s NSString’s stringWithString:aStr
    
return (aNSString’s stringByReplacingOccurrencesOfString:"[^\\U000024EA-\\U000024EA\\U00002460-\\U00002473\\U00003251-\\U000032BF\\U000024FF-\\U000024FF\\U00002776-\\U0000277F\\U000024EB-\\U000024F4\\U00002780-\\U00002789\\U0000278A-\\U00002793\\U000024F5-\\U000024FE]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, aNSString’s |length|()}) as text
  end holdNumberWithSignOnly
  
  
  
–丸つき数字を数値にデコードする v2
  
on decodeNumFromNumWithSign(aStr as string)
    set numStr1 to "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿"
    
set numStr2 to "❶❷❸❹❺❻❼❽❾❿⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴"
    
set numStr3 to "➀➁➂➃➄➅➆➇➈➉"
    
set numStr4 to "➊➋➌➍➎➏➐➑➒➓"
    
set numStr5 to "⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾"
    
    
set nList to {numStr1, numStr2, numStr3, numStr4, numStr5}
    
    
repeat with i in nList
      set numTemp to contents of i
      
if numTemp contains aStr then
        using terms from scripting additions
          set bNum to offset of aStr in numTemp
        end using terms from
        
return bNum
      end if
    end repeat
    
return false
  end decodeNumFromNumWithSign
  
  
–1D Listを指定デリミタをはさみつつテキストに
  
on retDelimedText(aList as list, aDelim as string)
    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 retDelimedText
end script

★Click Here to Open This Script 

Posted in list Text | Tagged 10.15savvy 11.0savvy 12.0savvy | 1 Comment

与えられた自然言語テキストから言語を推測して、指定の性別で、TTSキャラクタを自動選択して読み上げ

Posted on 2月 5, 2022 by Takaaki Naganoya

自然言語テキストを与えると、記述言語を推測して、その言語コード(jaとか)、性別、Premium(高音質)音声かどうか(true/false)をもとにText to Speechの読み上げ音声キャラクタをしぼりこんで、sayコマンドで音声読み上げするAppleScriptです。

# ScriptのリストについているURL Linkを書き換えました

自然言語から推測される言語コードと、TTS音声キャラクタに振られている言語コードの間に仕様的な食い違いがあるので、中国語の自動判定を行うためには、(若干の)処理を追加する必要があります。

自然言語テキストから取得できるのは「簡体字」「繁体字」のコードである一方で、TTS読み上げキャラクタが持っているのは、China、HongKong、Taiwanと国コードなので、対照表でもつけるか、いっそ全部「zh」でくくってランダム選択するか、、、はたまた、実行マシンの緯度・経度情報から判定するか、テーブルを編集可能なようにしておいて、テーブルのルールを決め打ちで反映するとか、、、、


▲システム環境設定>アクセシビリティ>読み上げコンテンツ>システムの声 のポップアップメニューで、一番下に「カスタマイズ」の項目があり、Text To Speech読み上げキャラクタの追加が行える


▲追加したTTSキャラクタの音声データは自動でダウンロードが行われる。TTS用にSiri音声は指定できないが、「ショートカット」の音声読み上げでは指定できる。このあたり、外部のTTS音声データ提供会社との契約によるものなのか、あるいは管理プログラムが異なるのか?

AppleScript名:与えられた自然言語テキストから言語を推測して、指定の性別で、TTSキャラクタを自動選択して読み上げ v1(簡体字、繁体字 未サポート).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/02/05
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "AppKit"

property NSSpeechSynthesizer : a reference to current application’s NSSpeechSynthesizer

set str1 to "こんにちは"

–指定文字列が何語かを推測して、言語コード(Short)を取得
set a1Res to guessLanguageCodeOf(str1) of me

–指定の言語コード(Short)をキーにしてTTS属性情報を取得
set vList to retAvailableTTSbyShortLangCodeAndSexAndPremium(a1Res, "Female", true) of me
if vList = {} then return

–取得したTTS情報リストから、てきとーに項目を取得
set fV to contents of first item of vList

set vName to VoiceName of fV
say str1 using vName

—
on retAvailableTTSbyShortLangCodeAndSexAndPremium(aLangShortCode as string, aSex as string, premiumFlag as boolean)
  set outList to {}
  
  
if aSex is not in {"Male", "Female"} then error "Sex code is wrong"
  
  
set aList to NSSpeechSynthesizer’s availableVoices()
  
set bList to aList as list
  
  
repeat with i in bList
    set j to contents of i
    
set aInfo to (NSSpeechSynthesizer’s attributesForVoice:j)
    
set aInfoRec to aInfo as record
    
    
–読み上げ対象文字データは多すぎるので削除しておく
    
set VoiceIndividuallySpokenCharacters of aInfoRec to {}
    
set VoiceSupportedCharacters of aInfoRec to {}
    
    
set aName to VoiceName of aInfoRec
    
set aLangCode to VoiceLocaleIdentifier of aInfoRec
    
    
set aGender to VoiceGender of aInfoRec
    
set aVID to VoiceIdentifier of aInfoRec
    
    
if (aLangCode starts with aLangShortCode) and (aGender = "VoiceGender" & aSex) then
      if premiumFlag = true then
        if aVID ends with "premium" then
          set the end of outList to aInfoRec
        end if
      else
        set the end of outList to aInfoRec
      end if
    end if
  end repeat
  
  
return outList
end retAvailableTTSbyShortLangCodeAndSexAndPremium

–文字列から言語を推測して言語名を返す
on guessLanguageOf(theString)
  set theTagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:{current application’s NSLinguisticTagSchemeLanguage} options:0
  
theTagger’s setString:theString
  
set languageID to theTagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return ((current application’s NSLocale’s localeWithLocaleIdentifier:"en")’s localizedStringForLanguageCode:languageID) as text
end guessLanguageOf

–文字列から言語を推測して言語コードを返す
on guessLanguageCodeOf(theString)
  set theTagger to current application’s NSLinguisticTagger’s alloc()’s initWithTagSchemes:{current application’s NSLinguisticTagSchemeLanguage} options:0
  
theTagger’s setString:theString
  
set languageID to theTagger’s tagAtIndex:0 |scheme|:(current application’s NSLinguisticTagSchemeLanguage) tokenRange:(missing value) sentenceRange:(missing value)
  
return languageID as text
end guessLanguageCodeOf

★Click Here to Open This Script 

Posted in Language Text Text to Speech | Tagged 10.15savvy 11.0savvy 12.0savvy NSLinguisticTagger NSLocale NSSpeechSynthesizer | Leave a comment

CotEditorで選択範囲の行頭にある数字をリナンバーする v1

Posted on 1月 2, 2022 by Takaaki Naganoya

CotEditorでオープン中の最前面の書類の選択範囲のテキストを行ごとにチェックし、行頭に存在する数字を、それらのうちの最小値を検出しつつ、指定のステップ数でリナンバー(番号振り直し)を行うAppleScriptです。

さまざまな項目の整理のために、テキストの先頭に仮想的なノンブル(ページ番号的なもの、ソート順を指定するための番号)を振っています。この番号ではじまるテキストをもとにFinder上でフォルダ整理をしており、項目の前後関係を入れ替えると…前後関係を明示するために、番号を振り直す必要が出てくるわけです。

# Numbers上やExcel上で行うと、余計な書体スタイルなどが入ってきて邪魔なので、テキストエディタ上で行うことが多いです

その番号の振り直しを行うAppleScriptです。macOS 12.2beta+CotEditor v4.0.9で動作確認を行っています。


▲選択範囲内の行頭の番号を振り直す。ちなみに、表示例はボツになった本の企画

本来は「行頭にある数字」を指示する必要があるものの、まだうまく機能していません。桁数でなんとなく判別しているだけです。


▲フォント作者の方々の合意を得られなさそうで流れた企画「同人フォントソムリエ」

AppleScript名:選択範囲の行頭にある数字をリナンバーする v1.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2022/01/02
—
–  Copyright © 2022 Piyomaru Software, All Rights Reserved
—

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

property NSMutableArray : a reference to current application’s NSMutableArray

property myNumStep : 1000

tell application "CotEditor"
  tell front document
    set aSel to contents of selection
    
set aSelList to paragraphs of aSel
  end tell
end tell

–範囲指定がない場合
if length of aSelList = 0 then
  display dialog "Error: No Selection" buttons {"OK"} default button 1 with icon 2
  
return
end if

–過大な範囲指定チェック
if length of aSelList > 1000 then
  set bRes to button returned of (display dialog "選択範囲が1000行を超えています。処理に時間がかかることが予想されますが、実行しますか?")
end if

–行頭の数字部分のみ取得して、数字の最小値を取得する
set topNumList to getNumbersAtLineBegennings(aSelList) of me
set minNum to calcIntMinAsStr(topNumList) of me

–行頭部分の数字部分のみリナンバー
set bRes to renumberNumsAtLineBegennings(aSelList, minNum as integer, myNumStep) of me

–1D Arrayを改行コードをデリミタに指定しつつテキスト化
set outStr to retDelimedText(bRes, return) of me

tell application "CotEditor"
  tell front document
    set contents of selection to outStr
  end tell
end tell

–行頭に入っている数字の文字のみリナンバー
–実際には行頭判定はまだ行えていない。行頭の番号と文の途中に出てくる数字の区別は「桁数」でのみ行っている
on renumberNumsAtLineBegennings(aSelList as list, firstNum as string, stepNum as number)
  set aDigit to length of firstNum –桁数
  
copy firstNum to aCount
  
  
set aRes to {}
  
  
repeat with i in aSelList
    set j to (contents of i) as string
    
set nRes to (getNumberCharsOnlyAtBegening(j) of me) as string
    
    
if nRes is not equal to "" then –InputとOutputが違った
      set n1Res to removeNumCharsOnly(j, aDigit) of me
      
set tmpNumStr to zeroPadding(aCount, aDigit) of me
      
set n1Res to tmpNumStr & n1Res
      
set aCount to aCount + stepNum
      
set the end of aRes to n1Res
    else
      set the end of aRes to j
    end if
  end repeat
  
  
return aRes
end renumberNumsAtLineBegennings

–1D Listの最小値を文字列で返す
on calcIntMinAsStr(aList as list)
  set nArray to (NSMutableArray’s arrayWithArray:aList)
  
set maxRes to (nArray’s valueForKeyPath:"@min.self")’s intValue()
  
return maxRes as string
end calcIntMinAsStr

–行頭に入っている数字の文字のみ抽出
on getNumbersAtLineBegennings(aSelList as list)
  set aRes to {}
  
repeat with i in aSelList
    set j to contents of i
    
set nRes to getNumberCharsOnlyAtBegening(j) of me
    
if nRes is not equal to "" then
      set the end of aRes to nRes
    end if
  end repeat
  
  
return aRes
end getNumbersAtLineBegennings

–数字のみ返す
on getNumberCharsOnlyAtBegening(aStr as string)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:"[^0-9]{4,}" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end getNumberCharsOnlyAtBegening

–数字のみ削除して返す
on removeNumCharsOnly(aStr as string, aDigit)
  set anNSString to current application’s NSString’s stringWithString:aStr
  
set anNSString to anNSString’s stringByReplacingOccurrencesOfString:("[0-9]{" & (aDigit as string) & ",}") withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, anNSString’s |length|()}
  
return anNSString as text
end removeNumCharsOnly

–指定桁数で指定の数にゼロパディングして文字列を返す
on zeroPadding(aNum as number, aDigit as number)
  set aText to "00000000000" & (aNum as text)
  
set aLen to length of aText
  
set aRes to text (aLen – aDigit + 1) thru -1 of aText
  
return aRes
end zeroPadding

–1D Listを指定デリミタをはさみつつテキストに
on retDelimedText(aList as list, aDelim as string)
  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 retDelimedText

★Click Here to Open This Script 

Posted in regexp Text | Tagged 10.15savvy 11.0savvy 12.0savvy CotEditor | Leave a comment

Keynoteの表の選択中のセルに入っている丸つき数字をインクリメント/デクリメント

Posted on 12月 16, 2021 by Takaaki Naganoya

Keynoteの最前面の書類の現在のスライド上にある表の選択中のセルに入っている丸つき数字(①②③…… )をインクリメント(+1)、デクリメント(ー1)するAppleScriptです。

–> Watch Demo Movie

書籍などに掲載する資料で、参照番号をいじくるツールがどうしても必要になって作ったものです。

macOS標準搭載のスクリプトメニューに入れて実行することを前提にしています。丸つき数字は、

①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿

のみを前提にして処理しています。その他の、

❶❷❸❹❺❻❼❽❾❿⓫⓬⓭⓮⓯⓰⓱⓲⓳⓴
➀➁➂➃➄➅➆➇➈➉
➊➋➌➍➎➏➐➑➒➓
⓵⓶⓷⓸⓹⓺⓻⓼⓽⓾

については無視しています。

Keynote v11.2+macOS 12.1betaで作成&動作確認していますが、Keynoteのバージョンにはとくに(バグのあるバージョンでなれけば)依存している機能はありません。


▲選択範囲のセルに入っている丸つき数字のインクリメント(①②③→②③④)


▲選択範囲のセルに入っている丸つき数字のデクリメント(②③④→①②③)

AppleScript名:選択中の表の指定行・列のマル付き数字のインクリメント(+1).scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2021/12/16
—
–  Copyright © 2021 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — macOS 10.13 or later
use framework "Foundation"
use scripting additions

–現在選択中の表オブジェクトを取得
set curTable to returnSelectedTableOnCurrentSlide() of me
if curTable = false then return

–現在選択中の表オブジェクト中の選択範囲中のセルをすべて取得(1D List)
using terms from application "Keynote"
  tell curTable
    set cellList to every cell of selection range
  end tell
end using terms from

–list中の各アイテムの冒頭に順次丸つき数字を追加する
using terms from application "Keynote"
  tell curTable
    repeat with i in cellList
      –選択範囲のセルの値を取り出す
      
set aVal to value of i
      
set vList to characters of (aVal as string)
      
      
–1文字ずつに分割してループ
      
set tmpOut to ""
      
repeat with ii in vList
        set jj to contents of ii
        
        
–丸付き数字の検出
        
set tmpR to holdNumberWithSignOnly(jj) of me
        
if tmpR is equal to jj then
          –丸付き数字を数値にデコード
          
set tmpNum to decodeNumFromNumWithSign(tmpR) of me
          
          
–数値をインクリメント
          
if tmpNum ≤ 50 then
            set tmpNum to tmpNum + 1 –インクリメント
          end if
          
          
–数値を丸付き数字にエンコード
          
set tmpR to convNumToNumWithSign(tmpNum) of me
          
set tmpOut to tmpOut & tmpR
        else
          set tmpOut to tmpOut & jj
        end if
      end repeat
      
      
set value of i to tmpOut
    end repeat
  end tell
end using terms from

–1~50の範囲の数値を丸つき数字に変換して返す
on convNumToNumWithSign(aNum as number)
  if (aNum ≤ 0) or (aNum ≥ 50) then return ""
  
set aStr to "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿"
  
set bChar to character aNum of aStr
  
return bChar
end convNumToNumWithSign

–指定文字列から丸つき数字のみ抽出する
on holdNumberWithSignOnly(aStr as text)
  set aNSString to current application’s NSString’s stringWithString:aStr
  
return (aNSString’s stringByReplacingOccurrencesOfString:"[^\\U000024EA-\\U000024EA\\U00002460-\\U00002473\\U00003251-\\U000032BF\\U000024FF-\\U000024FF\\U00002776-\\U0000277F\\U000024EB-\\U000024F4\\U00002780-\\U00002789\\U0000278A-\\U00002793\\U000024F5-\\U000024FE]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, aNSString’s |length|()}) as text
end holdNumberWithSignOnly

–丸つき数字を数値にデコードする v2(簡略版)
on decodeNumFromNumWithSign(aStr as string)
  set numStr1 to "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿"
  
  
if numStr1 contains aStr then
    using terms from scripting additions
      set bNum to offset of aStr in numStr1
    end using terms from
    
return bNum
  end if
  
return false
end decodeNumFromNumWithSign

–現在のスライド上で選択中の表オブジェクトへの参照を取得する
on returnSelectedTableOnCurrentSlide()
  tell application "Keynote"
    tell front document
      tell current slide
        try
          set theTable to first table whose class of selection range is range
        on error
          return false –何も選択されてなかった場合
        end try
        
        
return theTable
        
      end tell
    end tell
  end tell
end returnSelectedTableOnCurrentSlide

★Click Here to Open This Script 

AppleScript名:選択中の表の指定行・列のマル付き数字のデクリメント(ー1).scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2021/12/16
—
–  Copyright © 2021 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7" — macOS 10.13 or later
use framework "Foundation"
use scripting additions

–現在選択中の表オブジェクトを取得
set curTable to returnSelectedTableOnCurrentSlide() of me
if curTable = false then return

–現在選択中の表オブジェクト中の選択範囲中のセルをすべて取得(1D List)
using terms from application "Keynote"
  tell curTable
    set cellList to every cell of selection range
  end tell
end using terms from

–list中の各アイテムの冒頭に順次丸つき数字を追加する
using terms from application "Keynote"
  tell curTable
    repeat with i in cellList
      –選択範囲のセルの値を取り出す
      
set aVal to value of i
      
set vList to characters of (aVal as string)
      
      
–1文字ずつに分割してループ
      
set tmpOut to ""
      
repeat with ii in vList
        set jj to contents of ii
        
        
–丸付き数字の検出
        
set tmpR to holdNumberWithSignOnly(jj) of me
        
if tmpR is equal to jj then
          –丸付き数字を数値にデコード
          
set tmpNum to decodeNumFromNumWithSign(tmpR) of me
          
          
–数値をデクリメント
          
if tmpNum > 1 then
            set tmpNum to tmpNum – 1 –デクリメント
          end if
          
          
–数値を丸付き数字にエンコード
          
set tmpR to convNumToNumWithSign(tmpNum) of me
          
set tmpOut to tmpOut & tmpR
        else
          set tmpOut to tmpOut & jj
        end if
      end repeat
      
      
set value of i to tmpOut
    end repeat
  end tell
end using terms from

–1~50の範囲の数値を丸つき数字に変換して返す
on convNumToNumWithSign(aNum as number)
  if (aNum ≤ 0) or (aNum ≥ 50) then return ""
  
set aStr to "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿"
  
set bChar to character aNum of aStr
  
return bChar
end convNumToNumWithSign

–指定文字列から丸つき数字のみ抽出する
on holdNumberWithSignOnly(aStr as text)
  set aNSString to current application’s NSString’s stringWithString:aStr
  
return (aNSString’s stringByReplacingOccurrencesOfString:"[^\\U000024EA-\\U000024EA\\U00002460-\\U00002473\\U00003251-\\U000032BF\\U000024FF-\\U000024FF\\U00002776-\\U0000277F\\U000024EB-\\U000024F4\\U00002780-\\U00002789\\U0000278A-\\U00002793\\U000024F5-\\U000024FE]" withString:"" options:(current application’s NSRegularExpressionSearch) range:{0, aNSString’s |length|()}) as text
end holdNumberWithSignOnly

–丸つき数字を数値にデコードする v2(簡略版)
on decodeNumFromNumWithSign(aStr as string)
  set numStr1 to "①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㉑㉒㉓㉔㉕㉖㉗㉘㉙㉚㉛㉜㉝㉞㉟㊱㊲㊳㊴㊵㊶㊷㊸㊹㊺㊻㊼㊽㊾㊿"
  
  
if numStr1 contains aStr then
    using terms from scripting additions
      set bNum to offset of aStr in numStr1
    end using terms from
    
return bNum
  end if
  
return false
end decodeNumFromNumWithSign

–現在のスライド上で選択中の表オブジェクトへの参照を取得する
on returnSelectedTableOnCurrentSlide()
  tell application "Keynote"
    tell front document
      tell current slide
        try
          set theTable to first table whose class of selection range is range
        on error
          return false –何も選択されてなかった場合
        end try
        
        
return theTable
        
      end tell
    end tell
  end tell
end returnSelectedTableOnCurrentSlide

★Click Here to Open This Script 

Posted in list Text | Tagged 10.15savvy 11.0savvy 12.0savvy Keynote | Leave a comment

漢字をパーツに分解して部品が共通する文字を検索

Posted on 12月 14, 2021 by Takaaki Naganoya

2つの漢字をパーツごとに分解し、それぞれの分解したパーツからそれを使用している漢字を検索。2つのグループ間で共通して存在している漢字を抽出することで「部品が共通する文字」をリストアップする処理を擬似的に実現しているAppleScriptです。

macOSの日本語入力プログラムで実装している、「部品が共通な漢字を検索」機能っぽいものを目指したものです。例によって、掲載プログラムリストでは部品が足りずに実行できないので、以下のすべての部品を含むScriptバンドルのアーカイブをダウンロードして実行してください。

–> Download listupKanjiWithCommonParts(including AppleScript Libraries and json data)

「際」「隆」という文字を指定すると、

--> {"際", "障", "隠", "隣", "阪", "防", "阻", "附", "降", "限", "陛", "院", "陣", "除", "陥", "陪", "腹", "堕", "陰", "墜", "陳", "陵", "陶", "陸", "険", "陽", "隅", "隆", "隊", "階", "随", "隔", "隙"}

という結果を返してきます。Mac mini M1で0.46秒ぐらいです。処理時間はデータによっては大幅に増える場合がありますが、そういうものだと思ってください。テストした範囲内では最悪で1.6秒ぐらいかかりました。もともとのJSONデータを(日本の)常用漢字の範囲内だけにシェイプアップすれば半分以下の速度で実行できると思います。

いろいろ細かく高速化のための記述していますが、実測値で0.02秒ぐらい速くなっているだけなので、わかりやすさを優先して高速化のための記述はすべて外してしまったほうがよいかもしれません。

割とロマン系の「役に立つんだかどうか不明だけど、作れることがわかったので作ってみた」プログラムです。

AppleScript名:漢字をパーツに分解して部品が共通する文字を検索.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2021/12/14
—
–  Copyright © 2021 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.7"
use framework "Foundation"
use scripting additions
use jkLib : script "jyoyoKanjiLib" –常用漢字一覧を返してくるAppleScriptライブラリ

script jsonStr
  property aJsonDict : missing value
  
property jKanji : missing value
  
property vOut : missing value
end script

script spd0
  property aList : {}
  
property bList : {}
  
property cList : {}
end script

property NSString : a reference to current application’s NSString
property NSCountedSet : a reference to current application’s NSCountedSet
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

set aTarg to "際"
set (aList of spd0) to getRelatedCharacters(aTarg) of me

set bTarg to "隆"
set (bList of spd0) to getRelatedCharacters(bTarg) of me

set (cList of spd0) to (aList of spd0) & (bList of spd0)

set cRes to returnDuplicatesOnly((cList of spd0)) of me
–> {"際", "障", "隠", "隣", "阪", "防", "阻", "附", "降", "限", "陛", "院", "陣", "除", "陥", "陪", "腹", "堕", "陰", "墜", "陳", "陵", "陶", "陸", "険", "陽", "隅", "隆", "隊", "階", "随", "隔", "隙"}

on returnDuplicatesOnly(aList as list)
  script spd1
    property bList : {}
    
property dupList : {}
  end script
  
  
set aSet to NSCountedSet’s alloc()’s initWithArray:aList
  
set (bList of spd1) to (aSet’s allObjects()) as list
  
  
set (dupList of spd1) to {}
  
repeat with i in (bList of spd1)
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of (dupList of spd1) to (contents of i)
    end if
  end repeat
  
  
return (dupList of spd1)
end returnDuplicatesOnly

on getRelatedCharacters(aTarg as string)
  set aVector to parseKanjiToParts(aTarg) of me
  
  
set (vOut of jsonStr) to {}
  
repeat with i in aVector
    set j to contents of i
    
set tmpOut to searchKanjiFromElementJ(j) of me
    
if tmpOut is not equal to missing value then
      set (vOut of jsonStr) to (vOut of jsonStr) & tmpOut
    end if
  end repeat
  
  
set tmp2 to makeUniqueListFrom((vOut of jsonStr)) of me
  
  
return tmp2
end getRelatedCharacters

on cleanUp1DList(aList as list, cleanUpItems as list)
  script spd2
    property bList : {}
  end script
  
  
set (bList of spd2) to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not in cleanUpItems then
      set the end of (bList of spd2) to j
    end if
  end repeat
  
return (bList of spd2)
end cleanUp1DList

on parseKanjiToParts(aTarg)
  set outList to {}
  
  
set kList to listupQueryKeysForKanji() of me
  
repeat with i in kList
    set j to contents of i
    
set aTargL to ((aJsonDict of jsonStr)’s valueForKey:j) as list
    
if aTarg is in aTargL then
      set the end of outList to j
    end if
  end repeat
  
return outList
end parseKanjiToParts

on searchKanjiFromElementJ(aQueryStr)
  if (aJsonDict of jsonStr) = missing value then my init()
  
if (jKanji of jsonStr) = missing value then my initJ()
  
set aRes to (aJsonDict of jsonStr)’s valueForKey:aQueryStr
  
if aRes = missing value then return missing value
  
  
set cArray to aRes’s arrayByAddingObjectsFromArray:(jKanji of jsonStr)
  
set cRes to returnDuplicatesOnly(cArray) of me
  
return cRes
end searchKanjiFromElementJ

on searchKanjiFromElement(aQueryStr)
  if (aJsonDict of jsonStr) = missing value then my init()
  
set aRes to (aJsonDict of jsonStr)’s valueForKey:aQueryStr
  
if aRes = missing value then return missing value
  
return aRes as list
end searchKanjiFromElement

on listupQueryKeysForKanji()
  if (aJsonDict of jsonStr) = missing value then my init()
  
set aRes to (aJsonDict of jsonStr)’s allKeys()
  
return aRes as list
end listupQueryKeysForKanji

–Pure AS風のパラメータ記述
on makeUniqueListFrom(theList)
  set theSet to current application’s NSOrderedSet’s orderedSetWithArray:theList
  
return (theSet’s array()) as list
end makeUniqueListFrom

on initJ()
  set (jKanji of jsonStr) to (current application’s NSArray’s arrayWithArray:(retJyouyouKanji() of jkLib))
end initJ

on init()
  –https://github.com/yagays/kanjivg-radical
  
set aPath to (POSIX path of (path to me)) & "Contents/Resources/element2kanji.json"
  
set jsonString to NSString’s alloc()’s initWithContentsOfFile:(aPath) encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set (aJsonDict of jsonStr) to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
end init

★Click Here to Open This Script 

Posted in JSON list Text | Tagged 10.15savvy 11.0savvy 12.0savvy | Leave a comment

構成要素を指定して漢字検索 v4

Posted on 12月 13, 2021 by Takaaki Naganoya

漢字の構成要素を指定して漢字を検索するAppleScriptです。

ライブラリやデータを含むAppleScriptバンドルをダウンロードできるようにしておきます。

–> Download kanjiSearchFromPartsV4 (Inlcuding AppleScript Libraries)

macOS標準搭載の「関連文字に変換」に似たような動作を行います。「関連文字に変換」では、いったん漢字を構成要素に分解し、分解した要素をもとに共通漢字を検索するユーザーインタフェースになっていますが、こちらはダイレクトに構成要素部品を示して、そこから共通部品を持つ文字を検索します。

 {"氵", "艹", "田"}

をパラメータに指定して実行すると、

--> {"藩"}

という結果を返します。同様に、

 {"⻖", "生"} 

をパラメータに指定して実行すると、

--> {"隆"}

という結果を出します。日本語を学習中の他国語の話者の方とか、子供、漢字をそろそろ思い出せなくなってきているお年寄りなどにニーズがあると思われるプログラムです。

元になっている「kanjiVG」データの特性なのですが、大人がよく理解して使う分には問題ないものの、義務教育過程の小中学生が使うと、学校で教える漢字部品の指定の仕方と異なるので、「宿題のために使えてもテストの点数が落ちる」といった弊害があるかもしれません。

あとは、GUIがあったほうが使いやすいプログラムだと思われるため、電卓(計算機)のようなイージーなインタフェースをつけて道具に仕立てたほうがよいのでしょう。

本プログラムリストは、スクリプトエディタに転送できますが、バンドル形式で保存してデータやライブラリを含んだ形にしないと実行できないため、実行のためには本記事冒頭にあるダウンロードリンクからバンドルのアーカイブをダウンロードしてください。

AppleScript名:構成要素を指定して漢字検索 v4.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2021/02/21
—
–  Copyright © 2021 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.7"
use framework "Foundation"
use scripting additions
use jkLib : script "jyoyoKanjiLib" –常用漢字一覧を返してくるAppleScriptライブラリ

property NSString : a reference to current application’s NSString
property NSCountedSet : a reference to current application’s NSCountedSet
property NSJSONSerialization : a reference to current application’s NSJSONSerialization
property NSUTF8StringEncoding : a reference to current application’s NSUTF8StringEncoding

script jsonStr
  property aJsonDict : missing value –JSONから読み取ったNSDictionary
  
property jKanji : missing value –常用漢字データ(NSArray)
end script

–図形としての構成要素を指定して漢字検索(厳密にいえば部首ではない)
set p1List to {"氵", "艹", "田"}
set c1Res to searchKanjiFromElementListJ(p1List) of me
–> {"藩"}

set p2List to {"⻖", "生"} –こざとへん。データ内のこざとへんとおおざとの区別がよくわからなかった
set c2Res to searchKanjiFromElementListJ(p2List) of me
–> {"隆"}

–検索に使える部首のキー文字の一覧を返す
–set qList to listupQueryKeysForKanji() of me
–> {"工", "棘", "左", "位", "婁", "攴", …}

on searchKanjiFromElementListJ(aQueryList as list)
  script spd
    property aList : {}
    
property bList : {}
  end script
  
  
set aLen to length of aQueryList
  
if aLen = 1 then return searchKanjiFromElementJ(first item of aQueryList) of me
  
  
set (aList of spd) to {}
  
set (bList of spd) to {}
  
repeat with i in aQueryList
    set j to contents of i
    
set tmpRes to searchKanjiFromElementJ(j) of me
    
set the end of (aList of spd) to tmpRes
  end repeat
  
  
return searchDuplicateItemsOnlyIn2DList((aList of spd)) of me
  
end searchKanjiFromElementListJ

on searchDuplicateItemsOnlyIn2DList(allList as list)
  set aLen to length of allList
  
  
set aList to contents of first item of allList
  
set bList to contents of rest of allList
  
  
repeat with i in bList
    
    
set j to contents of i
    
    
–aListとbListを連結したListで重複が存在するかをチェック
    
set aArray to (current application’s NSArray’s arrayWithArray:aList)
    
set bArray to (current application’s NSArray’s arrayWithArray:j)
    
set cArray to (aArray’s arrayByAddingObjectsFromArray:bArray)
    
    
set aList to returnDuplicatesOnly(cArray, aLen) of me
    
  end repeat
  
  
return aList
end searchDuplicateItemsOnlyIn2DList

on searchKanjiFromElementJ(aQueryStr)
  if (aJsonDict of jsonStr) = missing value then my init()
  
if (jKanji of jsonStr) = missing value then my initJ()
  
set aRes to (aJsonDict of jsonStr)’s valueForKey:aQueryStr
  
if aRes = missing value then return missing value
  
  
set cArray to aRes’s arrayByAddingObjectsFromArray:(jKanji of jsonStr)
  
set cRes to returnDuplicatesOnly(cArray) of me
  
return cRes
end searchKanjiFromElementJ

on searchKanjiFromElement(aQueryStr)
  if (aJsonDict of jsonStr) = missing value then my init()
  
set aRes to (aJsonDict of jsonStr)’s valueForKey:aQueryStr
  
if aRes = missing value then return missing value
  
return aRes as list
end searchKanjiFromElement

on listupQueryKeysForKanji()
  if (aJsonDict of jsonStr) = missing value then my init()
  
set aRes to (aJsonDict of jsonStr)’s allKeys()
  
return aRes as list
end listupQueryKeysForKanji

on initJ()
  set (jKanji of jsonStr) to current application’s NSArray’s arrayWithArray:(retJyouyouKanji() of jkLib)
end initJ

on init()
  –https://github.com/yagays/kanjivg-radical
  
set aPath to (POSIX path of (path to me)) & "Contents/Resources/element2kanji.json"
  
set jsonString to NSString’s alloc()’s initWithContentsOfFile:(aPath) encoding:(NSUTF8StringEncoding) |error|:(missing value)
  
set jsonData to jsonString’s dataUsingEncoding:(NSUTF8StringEncoding)
  
set (aJsonDict of jsonStr) to NSJSONSerialization’s JSONObjectWithData:jsonData options:0 |error|:(missing value)
end init

on returnDuplicatesOnly(anArray)
  set aSet to NSCountedSet’s alloc()’s initWithArray:anArray
  
set bList to (aSet’s allObjects()) as list
  
  
set dupList to {}
  
repeat with i in bList
    set aRes to (aSet’s countForObject:i)
    
if aRes > 1 then
      set the end of dupList to (contents of i)
    end if
  end repeat
  
  
return dupList
end returnDuplicatesOnly

★Click Here to Open This Script 

Posted in list Text | Tagged 10.15savvy 11.0savvy 12.0savvy | Leave a comment

アンダースコアが入っていたら削除して次の文字を大文字化

Posted on 12月 6, 2021 by Takaaki Naganoya

文字列の途中にアンダースコア(「_」)が入っていたら削除し、その次の文字列(たぶんアルファベット)を大文字化するAppleScriptです。

github上でたまに「あ、これいいかも」というObjective-Cで書かれたプログラムを見つけた場合に、Cocoa Frameworkとしてビルドして外部からAppleScriptから呼び出したくなります。

しかし、メソッド名にアンダースコア(「_」)が入っていて断念したことが、何回かあります。

AppleScript、とくにCocoaを呼び出すAppleScriptObjCの世界では、そうしたCocoa Framework中のメソッドにアンダースコアが入っていると、「|」でエスケープしても展開されて、パラメータがそこに入るものとして解釈されてしまいます。つまり、すべてのソースを書き換えなくてはならないわけです。

ここで、単純にアンダースコアを削除しただけでは可読性が落ちるので、アンダースコアの後にあった文字(おそらくアルファベット)を大文字化したいわけですが、なかなか手が混んでいて大変な処理です。

そこで、AppleScriptを書いて処理させるためのテストプログラムを作ってみました。1行分の処理はできているように見えます。

この種類のプログラムは、何回か組んだ記憶があるのですが、いつも大して役に立たずに立ち消えになっているような気が、、、、やっぱり、Xcode自体のAppleScript対応度が散々な出来で、やりたい処理ができずにどこかに消えてしまうから、なのかも、、、、

AppleScript名:アンダースコアが入っていたら削除して次の文字を大文字化.scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2021/12/06
—
–  Copyright © 2021 Piyomaru Software, All Rights Reserved
—
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set a to "- (void)patch_splitMax_toMax:(NSMutableArray *)patches;"
set aTarg to "_"

set b to removeAcharAndCapitalize(a, aTarg) of me
–> "- (void)patchSplitMaxToMax:(NSMutableArray *)patches;"

on removeAcharAndCapitalize(aStr, aTargChar)
  if aStr contains aTargChar then
    set bList to countAcharInStrAndDetectPos(aStr, aTargChar) of me –1行に複数のアンダースコアが登場する場合に対処
    
copy bList to {countNum, itemList}
    
    
set revList to reverse of itemList –後方から処理
    
    
repeat with i in revList –アイテム番号でループ(後方から処理)
      set a1Str to text 1 thru (i – 1) of aStr
      
set bStr to text (i + 1) thru -1 of aStr
      
set bStr2 to capitalizeHeadChar(bStr) of me
      
set aStr to a1Str & bStr2
    end repeat
  end if
  
  
return aStr
end removeAcharAndCapitalize

–指定文字列の頭文字を大文字化
on capitalizeHeadChar(aStr)
  set a1Str to text 1 of aStr
  
set a2Str to text 2 thru -1 of aStr
  
  
set a1aStr to current application’s NSMutableString’s stringWithString:a1Str
  
set a1bStr to (a1aStr’s uppercaseString()) as string
  
  
return (a1bStr & a2Str)
end capitalizeHeadChar

–文字列中に指定文字が何個入っているかカウントし、登場位置をリストで返す
on countAcharInStrAndDetectPos(aStr, aTargChar)
  set aList to {}
  
set posC to 1
  
  
considering case
    set aHit to offset of aTargChar in aStr
    
    
if aHit is not equal to 0 then
      set aaList to characters of aStr
      
set aCount to 0
      
      
repeat with i in aaList
        
        
set j to contents of i
        
        
if j = aTargChar then
          set aCount to aCount + 1
          
set the end of aList to posC
        end if
        
        
set posC to posC + 1
        
      end repeat
      
      
return {aCount, aList}
    else
      return {0, {}}
    end if
  end considering
end countAcharInStrAndDetectPos

★Click Here to Open This Script 

Posted in Text | Tagged 10.15savvy 11.0savvy 12.0savvy | Leave a comment

Post navigation

  • Older posts
  • Newer posts

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

Google Search

Popular posts

  • 開発機としてM2 Mac miniが来たのでガチレビュー
  • CotEditorで2つの書類の行単位での差分検出
  • macOS 15, Sequoia
  • 指定のWordファイルをPDFに書き出す
  • Pages本執筆中に、2つの書類モード切り替えに気がついた
  • Numbersで選択範囲のセルの前後の空白を削除
  • メキシカンハットの描画
  • Pixelmator Pro v3.6.4でAppleScriptからの操作時の挙動に違和感が
  • AdobeがInDesign v19.4からPOSIX pathを採用
  • AppleScriptによる並列処理
  • Safariで「プロファイル」機能を使うとAppleScriptの処理に影響
  • Cocoa Scripting Course 続刊計画
  • macOS 14.xでScript Menuの実行速度が大幅に下がるバグ
  • AppleScript入門③AppleScriptを使った「自動化」とは?
  • NaturalLanguage.frameworkでNLEmbeddingの処理が可能な言語をチェック
  • Keynote/Pagesで選択中の表カラムの幅を均等割
  • Keynote、Pages、Numbers Ver.14.0が登場
  • macOS 15 リモートApple Eventsにバグ?
  • デフォルトインストールされたフォント名を取得するAppleScript
  • AppleScript入門① AppleScriptってなんだろう?

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (193) 14.0savvy (145) 15.0savvy (125) CotEditor (66) Finder (51) iTunes (19) Keynote (116) 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 (76) Pages (54) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

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

アーカイブ

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

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

メタ情報

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

Forum Posts

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

メタ情報

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