2018.02.26 Mon

digital media

複数キーワードを検索してPDFに注釈をつける

#PDF, #Mac, #AppleScript

author:Noriaki Fujita

こんにちは、CMB室の藤田です。

先日、複数のキーワードから PDF に注釈をつけられないかという相談があり、いろいろ調べてみたところこんな方法があることを知りました。

● Adobe Acrobat の「高度な検索」を使用して PDF に注釈を追加

❶ Adobe Acrobat(DC または Reader)で PDF を開き「編集メニュー > 高度な検索 」を選択します。
高度な検索

編集メニュー > 高度な検索

❷ 高度な検索ウィンドウが開きますのでウィンドウ下部の「詳細オプションを表示」をクリックします。
高度な検索ウィンドウ(左側)

高度な検索ウィンドウ(左側)

❸ 検索するキーワードを半角スペースで区切り、語句を入力します。「次の条件を含む」プルダウンからは「いずれかの語が一致するものを検索」を選択します。この状態で検索ボタンを押すと…
検索キーワードを入力して検索ボタンを押すと

検索キーワードを入力して検索ボタンを押すと

❹ 検索結果が表示されます。検索結果を Shift キーまたは⌘(Mac)、Ctrl(Windows)を押しながら複数選択してリターンキーを押すと…
検索結果を複数選択し、リターンキーを押すと

検索結果を複数選択し、リターンキーを押すと

❺ 検索語句が選択状態になります! この状態で「テキストをハイライト表示」ボタンを押すと…
選択状態で「テキストをハイライト表示」ボタンを押すと

選択状態で「テキストをハイライト表示」ボタンを押すと

❻ 選択されたすべての検索語句にハイライト注釈が追加されます!
ハイライト注釈が追加される!

ハイライト注釈が追加される!

この方法は Adobe のフォーラムで見つけて目から鱗が落ちました。リターンキーで複数選択ができることは Acrobat のヘルプでは見つけられませんでしたので裏技(?)的な活用法といえます。

● AppleScript で PDF に注釈を追加

とはいえ、上記の方法では注釈がいくつあってもページごとに1つのオブジェクトとして扱われてしまいます。

注釈は1つのオブジェクトになってしまう

注釈は1つのオブジェクトになってしまう

また、複数ファイルの PDF に対しては検索はできるものの、注釈を追加するにはファイルごとの作業となります。ただ注釈を追加したいだけならこれでもいいのでしょうが、せっかくなのでスクリプトで似たようなことはできないものかと思い試行錯誤してつくってみました。

このスクリプトは
① Excel で選択されたセルの値をキーワードに
② PDF 内の文字列を検索して
Acrobat を使用せずにテキストハイライト注釈を追加する
という仕組みになっています。複数ファイルの PDF にも対応した AppleScript ドロップレットです(Mac OS X 用)。キーワードの数に応じてプログレスバーがあらわれますが、概ね数秒で処理は終わります。

● さくいん用にログ書き出し機能

さらに、処理が終わるとログを書き出す機能を追加しました(社内用のみ)。検索で見つかったキーワードが PDF のどこのページラベルにあるかをタブ区切りテキストで書き出します。タブ区切りテキストはキーワードとページとファイル名リストで構成されており Excel でも編集できるのでさくいん作りにも役に立ちます。
書き出したログを Excel で開いたところ

書き出したログを Excel で開いたところ

以前 InDesign で目次・索引を作成するスクリプトを公開しましたがこちらはその PDF 版といったところです。

このスクリプトはタクトシステムのHPで無料配布していますので、よろしかったらぜひダウンロードしてください。

● 技術的な話

Acrobat を使用せずに PDF に注釈を追加する仕組みは、Mac OS X の API である Cocoa に秘訣があります。私もこのあたりはまだあまり詳しくないのですが、Mac OS X10.10(Yosemite)以降であれば Objective-C を AppleScript から操作できるということで Cocoa の勉強も兼ねて試作してみました。

注釈を作成する部分のソースコードは以下の通りです。PDFAnnotaiton クラスは OS のバージョンによって挙動が変わるのでそれぞれの macOS に対応するよう使い分けています。

Yosemite(10.10)・El Captan(10.11)用

  1. use AppleScript version "2.4"
  2. use scripting additions
  3. use framework "Foundation"
  4. use framework "Quartz"
  5. on addAnnotToPDFByBounds(theBounds, keyword, userName)
  6. -- PDFAnnotationMarkup はmacOS 10.410.12
  7. set textAnnotation to (current application's PDFAnnotationMarkup's alloc()'s initWithBounds:theBounds)
  8. -- 注釈の設定
  9. (textAnnotation's setColor:(current application's NSColor's colorWithDeviceRed:1 green:1 blue:0 alpha:0.33))
  10. (textAnnotation's setMarkupType:0) -- 0:highlight 1:strikeOut 2:underline
  11. (textAnnotation's setContents:keyword) -- キーワード
  12. (textAnnotation's setUserName:(current application's NSString's stringWithString:userName)) -- 作成者
  13. (textAnnotation's setModificationDate:(current application's NSDate's |date|())) -- 更新日
  14. -- 注釈をページに追加する
  15. return textAnnotation
  16. end addAnnotToPDFByBounds
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
use framework "Quartz"

on addAnnotToPDFByBounds(theBounds, keyword, userName)
-- PDFAnnotationMarkup はmacOS 10.4–10.12
set textAnnotation to (current application's PDFAnnotationMarkup's alloc()'s initWithBounds:theBounds)

-- 注釈の設定
(textAnnotation's setColor:(current application's NSColor's colorWithDeviceRed:1 green:1 blue:0 alpha:0.33))
(textAnnotation's setMarkupType:0) -- 0:highlight 1:strikeOut 2:underline
(textAnnotation's setContents:keyword) -- キーワード
(textAnnotation's setUserName:(current application's NSString's stringWithString:userName)) -- 作成者
(textAnnotation's setModificationDate:(current application's NSDate's |date|())) -- 更新日

-- 注釈をページに追加する
return textAnnotation
end addAnnotToPDFByBounds

 

PDFAnnotationMarkup クラスを使用していますが、このクラスは廃止予定とのことで推奨されていません。なのでこちらのバージョン限定です。
検索でマッチした語句(keyword)のバウンディングボックス(theBounds)を取得し注釈を追加しています。

Sierra(10.12)用

  1. use AppleScript version "2.5"
  2. use scripting additions
  3. use framework "Foundation"
  4. use framework "Quartz"
  5. on addAnnotToPDFByBounds(theBounds, keyword, userName)
  6. set p1 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x), (theBounds's origin's y))))
  7. set p2 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x), (theBounds's origin's y) + (theBounds's |size|'s height))))
  8. set p3 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x) + (theBounds's |size|'s width), (theBounds's origin's y) + (theBounds's |size|'s height))))
  9. set p4 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x) + (theBounds's |size|'s width), (theBounds's origin's y))))
  10. ------------------------------------------------------------------------------------------------------------------------------
  11. -- 10.12 のみ
  12. set textAnnotation to (current application's PDFAnnotationMarkup's new)
  13. -- 注釈の設定
  14. (textAnnotation's setQuadrilateralPoints:{p2, p3, p1, p4})
  15. (textAnnotation's setValue:(current application's NSColor's colorWithDeviceRed:1 green:1 blue:0.0 alpha:0.33) forAnnotationKey:(current application's kPDFAnnotationKey_Color))
  16. (textAnnotation's setValue:(current application's NSColor's clearColor()) forAnnotationKey:(current application's kPDFAnnotationKey_InteriorColor))
  17. (textAnnotation's setValue:keyword forAnnotationKey:(current application's kPDFAnnotationKey_Contents))
  18. (textAnnotation's setValue:(current application's NSString's stringWithString:userName) forAnnotationKey:(current application's kPDFAnnotationKey_TextLabel)) -- 作成者
  19. (textAnnotation's setValue:(current application's NSDate's |date|()) forAnnotationKey:(current application's kPDFAnnotationKey_Date)) -- 更新日
  20. ------------------------------------------------------------------------------------------------------------------------------
  21. return textAnnotation
  22. end addAnnotToPDFByBounds
use AppleScript version "2.5"
use scripting additions
use framework "Foundation"
use framework "Quartz"

on addAnnotToPDFByBounds(theBounds, keyword, userName)
set p1 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x), (theBounds's origin's y))))
set p2 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x), (theBounds's origin's y) + (theBounds's |size|'s height))))
set p3 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x) + (theBounds's |size|'s width), (theBounds's origin's y) + (theBounds's |size|'s height))))
set p4 to (current application's NSValue's valueWithPoint:(current application's CGPointMake((theBounds's origin's x) + (theBounds's |size|'s width), (theBounds's origin's y))))
------------------------------------------------------------------------------------------------------------------------------
-- 10.12 のみ
set textAnnotation to (current application's PDFAnnotationMarkup's new)
-- 注釈の設定
(textAnnotation's setQuadrilateralPoints:{p2, p3, p1, p4})
(textAnnotation's setValue:(current application's NSColor's colorWithDeviceRed:1 green:1 blue:0.0 alpha:0.33) forAnnotationKey:(current application's kPDFAnnotationKey_Color))
(textAnnotation's setValue:(current application's NSColor's clearColor()) forAnnotationKey:(current application's kPDFAnnotationKey_InteriorColor))
(textAnnotation's setValue:keyword forAnnotationKey:(current application's kPDFAnnotationKey_Contents))
(textAnnotation's setValue:(current application's NSString's stringWithString:userName) forAnnotationKey:(current application's kPDFAnnotationKey_TextLabel)) -- 作成者
(textAnnotation's setValue:(current application's NSDate's |date|()) forAnnotationKey:(current application's kPDFAnnotationKey_Date)) -- 更新日
------------------------------------------------------------------------------------------------------------------------------

return textAnnotation
end addAnnotToPDFByBounds

Apple のドキュメントによれば PDFAnnotationMarkup クラスは Sierra でも使用できるようなのですが、バウンディングボックスが設定できずうまくいきませんでした。なので setQuadrilateralPoints で注釈の四辺を設定するというかなり面倒な処理をしています。というか、initWithBounds が Sierra では使用できないということなのでしょうか。この辺まだまだ勉強不足です。

High Sierra(10.13)用

  1. use AppleScript version "2.7"
  2. use scripting additions
  3. use framework "Foundation"
  4. use framework "Quartz"
  5. on addAnnotToPDFByBounds(theBounds, keyword, userName)
  6. -- macOS 10.13以降で指定する形式のAnnotation
  7. set keyList to {current application's PDFAnnotationKeyColor, current application's PDFAnnotationKeyContents, current application's kPDFAnnotationKey_TextLabel, current application's kPDFAnnotationKey_Date}
  8. set valList to {current application's NSColor's colorWithDeviceRed:1 green:1 blue:0 alpha:0.33, keyword, (current application's NSString's stringWithString:userName), (current application's NSDate's |date|())}
  9. set propDict to (current application's NSMutableDictionary's dictionaryWithObjects:valList forKeys:keyList)
  10. set textAnnotation to (current application's PDFAnnotation's new's initWithBounds:theBounds forType:(current application's PDFAnnotationSubtypeHighlight) withProperties:propDict)
  11. return textAnnotation
  12. end addAnnotToPDFByBounds
use AppleScript version "2.7"
use scripting additions
use framework "Foundation"
use framework "Quartz"

on addAnnotToPDFByBounds(theBounds, keyword, userName)
-- macOS 10.13以降で指定する形式のAnnotation
set keyList to {current application's PDFAnnotationKeyColor, current application's PDFAnnotationKeyContents, current application's kPDFAnnotationKey_TextLabel, current application's kPDFAnnotationKey_Date}
set valList to {current application's NSColor's colorWithDeviceRed:1 green:1 blue:0 alpha:0.33, keyword, (current application's NSString's stringWithString:userName), (current application's NSDate's |date|())}
set propDict to (current application's NSMutableDictionary's dictionaryWithObjects:valList forKeys:keyList)

set textAnnotation to (current application's PDFAnnotation's new's initWithBounds:theBounds forType:(current application's PDFAnnotationSubtypeHighlight) withProperties:propDict)
return textAnnotation
end addAnnotToPDFByBounds

PDFAnnotationSubtypeHighlight を Type に指定し、色やテキストなどを配列で設定しています。今後のバージョンはこの方法で統一すると思われます。

共通
※ 注釈の色は黄色(R100G100B0)、不透明度は33%
※ 作成者名は作業マシンのログイン名(userName
※ 注釈のテキストメッセージは検索で一致した文字列(keyword
※ 注釈のタイムスタンプは処理を行った時刻(NSDate’s |date|()

上記のコードを本体スクリプトの Script Libraries に格納して呼び出す仕様となっています。

● 謝辞

このスクリプトを作成するにあたり、ぴよまるソフトウェアさんの「PDFでテキスト検索してURLリンクのアノテーションを追加する」を参考にさせていただきました。ありがとうございます。

Category

author

BackNumber

2021
2020
2019
2018
2017
2016

Copyright © TACT SYSTEM Co. Ltd. All Rights Reserved.公式サイトへ