Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Accessibility APIでテキストの内容、位置、サイズを取得する実装パターン

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Accessibility APIでテキストの内容、位置、サイズを取得する実装パターン

Avatar for Kishikawa Katsumi

Kishikawa Katsumi

September 29, 2026

More Decks by Kishikawa Katsumi

Other Decks in Programming

Transcript

  1. 事前準備 Privacy & SecurityのAccessibility (Device Control and Data Access)のSettings画面を開く let

    options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary let trusted = AXIsProcessTrustedWithOptions(options) if !trusted { ... }
  2. extension AXUIElement { func attr(_ el: AXUIElement, _ name: String)

    -> CFTypeRef? { var value: AnyObject? return AXUIElementCopyAttributeValue(self, name as CFString, &value) == .success ? value : nil } } extension AXUIElement { func string(_ name: String) -> String? { attr(self, name) as? String } func number(_ name: String) -> Int? { (attr(self, name) as? NSNumber)?.intValue } func element(_ name: String) -> AXUIElement? { attr(self, name).map { $0 as! AXUIElement } } func elements(_ name: String) -> [AXUIElement] { (attr(self, name) as? [AXUIElement]) ?? [] } }
  3. // 要素の取得(フォーカスされたウインドウ・子要素) let window = app.element(kAXFocusedWindowAttribute) let children = window?.elements(kAXChildrenAttribute)

    ?? [] // フォーカス中のウインドウ // 子要素 // プロパティの取得(Role・値テキスト・文字数) let role = element.string(kAXRoleAttribute) // "AXTextArea" など let text = element.string(kAXValueAttribute) // 本文テキスト let numOfChars = element.number(kAXNumberOfCharactersAttribute) // 文字数 extension AXUIElement { // 位置・サイズ } func frame(_ el: AXUIElement) -> CGRect? { guard let p = attr(self, kAXPositionAttribute), let s = attr(self, kAXSizeAttribute) else { return nil } var pt = CGPoint.zero, sz = CGSize.zero AXValueGetValue(p as! AXValue, .cgPoint, &pt) AXValueGetValue(s as! AXValue, .cgSize, &sz) return CGRect(origin: pt, size: sz) }
  4. ツリー構造をたどる 再帰的にkAXChildrenAttributeを取得する func dump(_ el: AXUIElement, _ depth: Int =

    0) { print(String(repeating: " ", count: depth) + (el.string(kAXRoleAttribute) ?? "?")) for c in el.elements(kAXChildrenAttribute) { dump(c, depth + 1) } } dump(window!)
  5. 目的 構造を把握して目的のUI要素を取得して操作する 関数 属性の取得 (role/value/children/position...) AXUIElementCopyAttributeValue 要素から取れる属性を調べる AXUIElementCopyAttributeNames 要素の値が変更可能かどうか AXUIElementIsAttributeSettable

    要素の値を変更する AXUIElementSetAttributeValue 引数が必要な属性の取得(例: AXBoundsForRange) AXUIElementCopyParameterizedAttributeValue 要素から取れる引数が必要な属性を調べる AXUIElementCopyParameterizedAttributeNames 座標の位置にある要素を取得 AXUIElementCopyElementAtPosition 複数属性をまとめて取得(高速化) AXUIElementCopyMultipleAttributeValues ルート要素の生成 AXUIElementCreateApplication(pid) / *SystemWide() 変化の通知 AXObserver*(AXObserverCreate など)
  6. • AXWindow AXStandardWindow title="sample.rtfd" • AXScrollArea • AXTextArea value="MacでVoiceOverを使い始める⏎VoiceOverをすぐに使い始めるため…" •

    AXImage AXTextAttachment • AXImage AXTextAttachment • AXLink AXTextLink title="Siriを使う方法" • AXLink AXTextLink title="「メニューバー」設定を変更する" • AXScrollBar • AXScrollBar • AXRuler • AXRulerMarker • AXRulerMarker • AXRulerMarker • AXGroup desc="Formatting" • AXMenuButton desc="paragraph style" • AXGroup • AXCheckBox AXSegment desc="bold" • AXCheckBox AXSegment desc="italic" • AXCheckBox AXSegment desc="underline" • AXCheckBox AXSegment desc="strikethrough" • AXButton value="dark gray" desc="text color" • AXButton value="Clear" desc="text background color" • AXPopUpButton value="Hiragino Sans" desc="typeface" • AXPopUpButton value="W4" desc="style" ... chars=881
  7. • AXWindow AXStandardWindow title="sample.rtfd" • AXScrollArea • AXTextArea value="MacでVoiceOverを使い始める⏎VoiceOverをすぐに使い始めるため…" •

    AXImage AXTextAttachment • AXImage AXTextAttachment • AXLink AXTextLink title="Siriを使う方法" • AXLink AXTextLink title="「メニューバー」設定を変更する" • AXScrollBar • AXScrollBar • AXRuler • AXRulerMarker • AXRulerMarker • AXRulerMarker • AXGroup desc="Formatting" • AXMenuButton desc="paragraph style" • AXGroup • AXCheckBox AXSegment desc="bold" • AXCheckBox AXSegment desc="italic" • AXCheckBox AXSegment desc="underline" • AXCheckBox AXSegment desc="strikethrough" • AXButton value="dark gray" desc="text color" • AXButton value="Clear" desc="text background color" • AXPopUpButton value="Hiragino Sans" desc="typeface" • AXPopUpButton value="W4" desc="style" ... chars=881
  8. UIやコンテンツのテキストを取得する 構造を把握して目的のUI要素を取得して操作する UIコンポーネント ラベル/表示テキスト テキストが入っている要素・属性 AXStaticText の AXValue 例 Finderのサイドバー項目、列の値、多く

    のラベル 編集可能テキストエリア AXTextField / AXTextAreaのAXValue TextEdit本文、検索フィールド、Finder のファイル名 ボタン AXButtonのAXTitleやAXDescription ボタン・ツールバーボタン SwiftUIのラベルなど AXGroup のAXDescription Messagesの本文、一部のSwiftUI
  9. • AXWindow AXStandardWindow title="macOS native Symposium 12 ライブ配信URLのご案内 —…" •

    AXGroup • AXScrollArea desc="message content" • AXGroup desc="message content" • AXGroup desc="message headers" ... • AXScrollArea • AXGroup • AXGroup • AXScrollArea • AXWebArea • AXLink • AXImage • AXLink title="macOS native Symposium #12 Sep 26, 2:00 …" • AXGroup • AXStaticText value="macOS native Symposium #12" • AXGroup • AXStaticText value="Sep 26, 2:00 PM GMT+9 · Apple Japan LLC" • AXLink • AXImage • AXHeading title="macOS native Symposium 12 ライブ配信URLのご案内" • AXGroup • AXStaticText • AXImage • AXGroup • AXStaticText • AXGroup • AXStaticText • AXGroup • AXStaticText ... value="macOS native Symposium 12 ライブ配信URLのご案内" value="1024jp" value="こんばんは! macOS native主催の1024jpです。 いよいよmacO…" value="当日の登壇内容のライブ配信のURLはこちらになります:"
  10. • AXWindow AXStandardWindow title="macOS native Symposium 12 ライブ配信URLのご案内 —…" •

    AXGroup • AXScrollArea desc="message content" • AXGroup desc="message content" • AXGroup desc="message headers" ... • AXScrollArea • AXGroup • AXGroup • AXScrollArea • AXWebArea • AXLink • AXImage • AXLink title="macOS native Symposium #12 Sep 26, 2:00 …" • AXGroup • AXStaticText value="macOS native Symposium #12" • AXGroup • AXStaticText value="Sep 26, 2:00 PM GMT+9 · Apple Japan LLC" • AXLink • AXImage • AXHeading title="macOS native Symposium 12 ライブ配信URLのご案内" • AXGroup • AXStaticText • AXImage • AXGroup • AXStaticText • AXGroup • AXStaticText • AXGroup • AXStaticText ... value="macOS native Symposium 12 ライブ配信URLのご案内" value="1024jp" value="こんばんは! macOS native主催の1024jpです。 いよいよmacO…" value="当日の登壇内容のライブ配信のURLはこちらになります:"
  11. UIやコンテンツのテキストを取得する 構造を把握して目的のUI要素を取得して操作する ‒ AXUIElementはツリー構造なのでツリーを辿って構造を出力する ‒ Native (AppKitなど)のUIコンポーネントは kAXTextAreaRoleなど特定の UI要素のkAXValueや kAXDescriptionを取得する

    ‒ WebKitを表示に使うアプリケーション(Safari、Mail、Notesなど)は AXWebAreaのkAXStaticTextRoleの要素の値を集める ‣ 末端の葉ノードまでたどらないとテキストが取れないので構造やヒューリス ティックな方法で適切にグループ化する
  12. Text Marker API func attr(_ e: AXUIElement, _ n: String)

    -> CFTypeRef? { var v: CFTypeRef? return AXUIElementCopyAttributeValue(e, n as CFString, &v) == .success ? v : nil } func attr(_ e: AXUIElement, _ n: String, with arg: CFTypeRef) -> CFTypeRef? { var v: CFTypeRef? return AXUIElementCopyParameterizedAttributeValue(e, n as CFString, arg, &v) == .success ? v : nil } let start = attr(webArea, "AXStartTextMarker")! let end = attr(webArea, "AXEndTextMarker")! let range = attr( webArea, "AXTextMarkerRangeForUnorderedTextMarkers", with: [start, end] as CFArray )! let text = attr(webArea, "AXStringForTextMarkerRange", with: range) as? String
  13. 目的 AXWebArea Native Component 構造を把握して目的のUI要素を取得して操作する 全文 AXStringForTextMarkerRange(start→end) AXValue 部分文字列 AXStringForTextMarkerRange

    AXStringForRange 範囲 → 矩形 AXBoundsForTextMarkerRange AXBoundsForRange 行の範囲 AXLineTextMarkerRangeForTextMarker AXLineForIndex / AXRangeForLine 座標 → 位置 AXTextMarkerForPosition AXRangeForPosition スタイル属性付き文字列 AXAttributedStringForTextMarkerRange AXAttributedStringForRange
  14. まとめ ドキュメントが乏しく、リバースエンジニアリング必須 ‒ Accessibility Client API (AXUIElement)は高レベルなAPIではないので、操 作対象によって適切に使い分ける必要がある ‒ ドキュメントは少なく、特にWebのコンテンツを取得するためのText

    Marker APIは完全にUndocumentedでWebKitの実装から推測する ‒ Discordの翻訳アプリなど、特定のアプリを対象にした小規模なユーティリティ なら実用的なものが作れるが、 ‒ スクリーンリーダーなどの開発など汎用的なアクセシビリティ支援ソフトウェ アを作る際には大きな障壁となる
  15. 付録 Text Marker APIの概要 名前 型 用途 AXStartTextMarker Marker 文書の先頭を示すマーカー

    AXEndTextMarker Marker 文書の末尾を示すマーカー AXSelectedTextMarkerRange Range 選択中のテキストの範囲 AXTextInputMarkedTextMarkerRange Range IME変換中の未確定範囲 AXUIElementCopyAttributeValueで取れる属性
  16. 付録 Text Marker APIの概要 名前 引数と戻り値の型 AXTextMarkerRangeForUnorderedTextMarkers 用途 [Marker,Marker] →

    Range 2点を範囲に束ねる AXTextMarkerForPosition CGPoint → Marker 画面座標 → マーカー (Chromeでは未サポート) AXStartTextMarkerForBounds / AXEndTextMarkerForBounds CGRect → Marker 矩形→マーカー AXUIElementForTextMarker Marker → AXUIElement マーカー位置の要素を得る AXTextMarkerForIndex / AXIndexForTextMarker 相互変換(主に Chrome) 文字インデックス ⇔ マーカー AXUIElementCopyParameterizedAttributeValue で取れる属性
  17. 付録 Text Marker APIの概要 名前 引数と戻り値の型 用途 AXStringForTextMarkerRange Range →

    String 範囲の文字列 AXAttributedStringForTextMarkerRange Range → スタイル属性付き文字列 スタイル付きテキスト AXBoundsForTextMarkerRange Range → CGRect 範囲の画面矩形 AXLengthForTextMarkerRange Range → 数値 範囲の文字数 AXUIElementCopyParameterizedAttributeValue で取れる属性
  18. 付録 Text Marker APIの概要 名前 用途 AXNextTextMarkerForTextMarker / AXPreviousTextMarkerForTextMarker 1文字ぶん次/前へ

    AXNextWordEndTextMarkerForTextMarker / AXPreviousWordStartTextMarkerForTextMarker 単語末/単語頭へ AXNextLineEndTextMarkerForTextMarker / AXPreviousLineStartTextMarkerForTextMarker 行末/行頭へ AXNextSentenceEndTextMarkerForTextMarker / AXPreviousSentenceStartTextMarkerForTextMarker 文末/文頭へ AXNextParagraphEndTextMarkerForTextMarker / AXPreviousParagraphStartTextMarkerForTextMarker 段落末/段落頭へ AXUIElementCopyParameterizedAttributeValue で取れる属性
  19. 付録 Text Marker APIの概要 名前 用途 AXWord* (Left/RightWordTextMarkerRangeForTextMarker) マーカー位置の単語範囲 AXLineTextMarkerRangeForTextMarker(Left/Right)

    視覚行の範囲 AXSentenceTextMarkerRangeForTextMarker 文の範囲 AXParagraphTextMarkerRangeForTextMarker 段落の範囲 AXStyleTextMarkerRangeForTextMarker 同一スタイルが続く範囲 AXTextMarkerRangeForUIElement ある要素に対応する範囲 AXLineForTextMarker / AXTextMarkerRangeForLine 行番号⇔行レンジ AXTextMarkerIsValid マーカーの有効性チェック AXUIElementCopyParameterizedAttributeValue で取れる属性
  20. 参考資料 ‒ Sample Code https://github.com/kishikawakatsumi/AXExamples ‒ try! Swift 2024 Sample

    code and presentation https://github.com/kishikawakatsumi/tryswift2024 ‒ Text Chat Translator https://github.com/kishikawakatsumi/TextChatTranslator
  21. 参考資料 ‒ [Proof of Concept]: Vosh - a third-party screen-reader

    for the Macintosh https://www.applevis.com/forum/app-development-programming/work-progress-vosh-third-party-screen-reader-macintosh ‒ Vosh - a third-party screen-reader for the Macintosh https://github.com/Choominator/Vosh ‒ The State of Screen readers in macOS https://www.applevis.com/forum/macos-mac-apps/state-screen-readers-macos