クリップボードにコピーしておいた書式つきテキストを、行ごとに画面上の描画サイズで昇順ソートし、再結合してクリップボードに転送するAppleScriptです。
ほぼ毎日使っているAppleScriptの改良版です。
Cocoaの機能を呼び出すと、AppleScript冒頭にproperty宣言文を書くことになりますが(書かなくてもいいんですが)、この宣言文を「プロポーショナルフォントを考慮しつつ実際の画面上の描画サイズを考慮」して短いものから長いものにソートして掲載しています。
ただし、従来のバージョンでは実際のフォントではなく一律に指定フォントで描画したさいの描画幅でソートしていたため、ごくまれに掲載時のリストが短い順になっていませんでした(世界中で誰も気にしてねえよ! ^ー^;)。
これを、実際の指定フォントやサイズを考慮した行単位のスタイル付きテキストに分解し、描画幅でソートしたあとにスタイル付きテキストを再合成するようにしました。
従来のOld Style AppleScriptではスタイル付きテキストの操作は一切できませんでしたが、Cocoaの機能を活用することでこのように自由度の高い処理ができるようになった、という好例です。
なお、本ルーチンによって厳密に描画幅でソートしても、Web掲載時にはWebブラウザのレンダリングのルール(&ユーザー側で指定しているWebブラウザのフォント)が適用されるため、macOS上のアプリケーション上での見た目と若干違いが出るという残念な結果も出ています。
macOS標準装備のスクリプトメニューに入れて呼び出しています。
AppleScript名:クリップボード内のRTFをStyled Stringとして解釈して行ごとに分割して画面描画サイズ幅で昇順ソートして再結合 |
— Created 2017-04-24 by Shane Stanley — Modified 2019-02-27 by Takaaki Naganoya use AppleScript version "2.5" use scripting additions use framework "Foundation" use framework "AppKit" use bPlus : script "BridgePlus" –https://www.macosxautomation.com/applescript/apps/BridgePlus.html property NSFont : a reference to current application’s NSFont property NSColor : a reference to current application’s NSColor property NSPasteboard : a reference to current application’s NSPasteboard property NSAttributedString : a reference to current application’s NSAttributedString 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 –クリップボードの内容をNSAttributedStringに set anAttr to my getClipboardASStyledText() if anAttr = missing value then return –Split Attributed Strings into lines set attrList to splitAttributedStringByLines(anAttr) of me –Sort by Width in ascending set sortedList to shellSortListAscending(attrList, 1) of me –Append Attributed Strings set mutableReturn to NSMutableAttributedString’s alloc()’s initWithString:(return) set outStr to NSMutableAttributedString’s alloc()’s initWithString:"" repeat with i in sortedList copy i to {tmpWidth, tmpAttr} (outStr’s appendAttributedString:tmpAttr) set tmpStr to tmpAttr’s |string|() as string if (tmpStr does not end with string id 10) and (tmpStr does not end with string id 13) then (outStr’s appendAttributedString:mutableReturn) end if end repeat — Set Styled String to Clipboard set theArray to {outStr} restoreClipboard(theArray) of me –Clipboardにデータを設定する on restoreClipboard(theArray as list) set thePasteboard to NSPasteboard’s generalPasteboard() thePasteboard’s clearContents() thePasteboard’s writeObjects:theArray end restoreClipboard — クリップボードの内容をNSAttributedStringとして取り出して返す on getClipboardASStyledText() try set theNSPasteboard to NSPasteboard’s generalPasteboard() set theAttributedStringNSArray to theNSPasteboard’s readObjectsForClasses:({NSAttributedString}) options:(missing value) set theNSAttributedString to theAttributedStringNSArray’s objectAtIndex:0 return theNSAttributedString on error return missing value end try end getClipboardASStyledText on splitAttributedStringByLines(theStyledText) set outList to {} set outAttr to NSMutableAttributedString’s alloc()’s initWithString:"" set thePureString to theStyledText’s |string|() –pure string from theStyledText set theLength to theStyledText’s |length|() set startIndex to 0 repeat until (startIndex = theLength) set {theAtts, theRange} to theStyledText’s attributesAtIndex:startIndex longestEffectiveRange:(reference) inRange:{startIndex, theLength – startIndex} set aText to (thePureString’s substringWithRange:theRange) as string –String set aColor to (theAtts’s valueForKeyPath:"NSColor") –Color set aFont to (theAtts’s valueForKeyPath:"NSFont") –Font if aFont is equal to missing value then error "Not font name and size are specified" –Font Name error set aDFontName to aFont’s displayName() –Font Name set aDFontSize to aFont’s pointSize() –Font Size set tmpAttrStr to generateAttributedString(aText, aDFontName, aDFontSize, aColor) of me outAttr’s appendAttributedString:tmpAttrStr if (aText contains return) or (aText contains string id 10) then –CR or LF set tmpSize to outAttr’s |size|() set tmpWidth to retWidthFromSize(tmpSize) of me set the end of outList to {tmpWidth, outAttr} set outAttr to NSMutableAttributedString’s alloc()’s initWithString:"" end if set startIndex to current application’s NSMaxRange(theRange) end repeat set tmpSize to outAttr’s |size|() set tmpWidth to retWidthFromSize(tmpSize) of me set the end of outList to {tmpWidth, outAttr} return outList end splitAttributedStringByLines on retWidthFromSize(tmpSize) if class of tmpSize = record then –macOS 10.10, 10.11, 10.12 set tmpWidth to width of tmpSize else –macOS 10.13, 10.14 set tmpWidth to first item of tmpSize end if return tmpWidth end retWidthFromSize on generateAttributedString(aStr, aFontPSName, aFontSize, aColor) set tmpAttr to NSMutableAttributedString’s alloc()’s initWithString:aStr set aRange to current application’s NSMakeRange(0, tmpAttr’s |length|()) set aVal1 to NSFont’s fontWithName:aFontPSName |size|:aFontSize tmpAttr’s beginEditing() tmpAttr’s addAttribute:(NSFontAttributeName) value:aVal1 range:aRange tmpAttr’s addAttribute:(NSForegroundColorAttributeName) value:aColor range:aRange tmpAttr’s endEditing() return tmpAttr end generateAttributedString –入れ子のリストを昇順ソート on shellSortListAscending(a, keyItem) return sort2DList(a, keyItem, {true}) of me end shellSortListAscending –入れ子のリストを降順ソート on shellSortListDecending(a, keyItem) return sort2DList(a, keyItem, {false}) of me end shellSortListDecending –2D Listをソート on sort2DList(aList as list, sortIndexes as list, sortOrders as list) load framework –index値をAS流(アイテムが1はじまり)からCocoa流(アイテムが0はじまり)に変換 set newIndex to {} repeat with i in sortIndexes set j to contents of i set j to j – 1 set the end of newIndex to j end repeat –Sort TypeのListを作成(あえて外部から指定する内容でもない) set sortTypes to {} repeat (length of sortIndexes) times set the end of sortTypes to "compare:" end repeat –Sort set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:newIndex ascending:sortOrders sortTypes:sortTypes |error|:(missing value)) as list return resList end sort2DList |